diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index dbdf64a779c..cacd81dcf36 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -27,7 +27,7 @@ jobs: - name: ruff run: | ruff --version - ruff . + ruff check . ruff format --check . - name: mypy diff --git a/pymatgen/alchemy/materials.py b/pymatgen/alchemy/materials.py index f3f5e1efeff..8c102e99143 100644 --- a/pymatgen/alchemy/materials.py +++ b/pymatgen/alchemy/materials.py @@ -344,19 +344,21 @@ def as_dict(self) -> dict[str, Any]: return dct @classmethod - def from_dict(cls, dct) -> TransformedStructure: + def from_dict(cls, dct: dict) -> TransformedStructure: """Creates a TransformedStructure from a dict.""" struct = Structure.from_dict(dct) return cls(struct, history=dct["history"], other_parameters=dct.get("other_parameters")) - def to_snl(self, authors, **kwargs) -> StructureNL: - """Generate SNL from TransformedStructure. + def to_snl(self, authors: list[str], **kwargs) -> StructureNL: + """ + Generate a StructureNL from TransformedStructure. - :param authors: List of authors - :param **kwargs: All kwargs supported by StructureNL. + Args: + authors (List[str]): List of authors contributing to the generated StructureNL. + **kwargs (Any): All kwargs supported by StructureNL. Returns: - StructureNL + StructureNL: The generated StructureNL object. """ if self.other_parameters: warn("Data in TransformedStructure.other_parameters discarded during type conversion to SNL") diff --git a/pymatgen/analysis/bond_dissociation.py b/pymatgen/analysis/bond_dissociation.py index 96c1d853285..9bc8e77768d 100644 --- a/pymatgen/analysis/bond_dissociation.py +++ b/pymatgen/analysis/bond_dissociation.py @@ -3,6 +3,7 @@ from __future__ import annotations import logging +import warnings import networkx as nx from monty.json import MSONable @@ -48,7 +49,7 @@ def __init__( Args: molecule_entry (dict): Entry for the principle molecule. Should have the keys mentioned above. - fragment_entries (list of dicts): List of fragment entries. Each should have the keys mentioned above. + fragment_entries (list[dict]): Fragment entries. Each should have the keys mentioned above. allow_additional_charge_separation (bool): If True, consider larger than normal charge separation among fragments. Defaults to False. See the definition of self.expected_charges below for more specific information. @@ -98,7 +99,7 @@ def __init__( self.fragment_and_process(bonds) # If multibreak, loop through pairs of ring bonds. if multibreak: - print( + warnings.warn( "Breaking pairs of ring bonds. WARNING: Structure changes much more likely, meaning dissociation values" " are less reliable! This is a bad idea!" ) @@ -120,6 +121,7 @@ def fragment_and_process(self, bonds): try: frags = self.mol_graph.split_molecule_subgraphs(bonds, allow_reverse=True) frag_success = True + except MolGraphSplitError: # If split is unsuccessful, then we have encountered a ring bond if len(bonds) == 1: @@ -152,13 +154,13 @@ def fragment_and_process(self, bonds): pb_mol = bb.pybel_mol smiles = pb_mol.write("smi").split()[0] specie = nx.get_node_attributes(self.mol_graph.graph, "specie") - print( + warnings.warn( f"Missing ring opening fragment resulting from the breakage of {specie[bonds[0][0]]} " f"{specie[bonds[0][1]]} bond {bonds[0][0]} {bonds[0][1]} which would yield a " f"molecule with this SMILES string: {smiles}" ) elif len(good_entries) == 1: - # If we have only one good entry, format it and add it to the list that will eventually return: + # If we have only one good entry, format it and add it to the list that will eventually return self.bond_dissociation_energies += [self.build_new_entry(good_entries, bonds)] else: # We shouldn't ever encounter more than one good entry. @@ -166,9 +168,9 @@ def fragment_and_process(self, bonds): elif len(bonds) == 2: raise RuntimeError("Should only be trying to break two bonds if multibreak is true! Exiting...") else: - print("No reason to try and break more than two bonds at once! Exiting...") - raise ValueError + raise ValueError("No reason to try and break more than two bonds at once! Exiting...") frag_success = False + if frag_success: # If the principle did successfully split, then we aren't dealing with a ring bond. # As above, we begin by making sure we haven't already encountered an identical pair of fragments: @@ -203,14 +205,14 @@ def fragment_and_process(self, bonds): smiles = pb_mol.write("smi").split()[0] for charge in self.expected_charges: if charge not in frag1_charges_found: - print(f"Missing {charge=} for fragment {smiles}") + warnings.warn(f"Missing {charge=} for fragment {smiles}") if len(frag2_charges_found) < len(self.expected_charges): bb = BabelMolAdaptor(frags[1].molecule) pb_mol = bb.pybel_mol smiles = pb_mol.write("smi").split()[0] for charge in self.expected_charges: if charge not in frag2_charges_found: - print(f"Missing {charge=} for fragment {smiles}") + warnings.warn(f"Missing {charge=} for fragment {smiles}") # Now we attempt to pair fragments with the right total charge, starting with only fragments with no # structural change: for frag1 in frag1_entries[0]: # 0 -> no structural change @@ -241,7 +243,7 @@ def fragment_and_process(self, bonds): self.bond_dissociation_energies += [self.build_new_entry([frag1, frag2], bonds)] n_entries_for_this_frag_pair += 1 - def search_fragment_entries(self, frag): + def search_fragment_entries(self, frag) -> list: """ Search all fragment entries for those isomorphic to the given fragment. We distinguish between entries where both initial and final molgraphs are isomorphic to the @@ -263,13 +265,14 @@ def search_fragment_entries(self, frag): final_entries += [entry] return [entries, initial_entries, final_entries] - def filter_fragment_entries(self, fragment_entries): + def filter_fragment_entries(self, fragment_entries: list) -> None: """ Filter the fragment entries. - :param fragment_entries: + Args: + fragment_entries (List): Fragment entries to be filtered. """ - self.filtered_entries = [] + self.filtered_entries: list = [] for entry in fragment_entries: # Check and make sure that PCM dielectric is consistent with principle: if "pcm_dielectric" in self.molecule_entry: @@ -305,8 +308,9 @@ def filter_fragment_entries(self, fragment_entries): else: entry["structure_change"] = "bond_change" found_similar_entry = False + # Check for uniqueness - for ii, filtered_entry in enumerate(self.filtered_entries): + for idx, filtered_entry in enumerate(self.filtered_entries): if filtered_entry["formula_pretty"] == entry["formula_pretty"] and ( filtered_entry["initial_molgraph"].isomorphic_to(entry["initial_molgraph"]) and filtered_entry["final_molgraph"].isomorphic_to(entry["final_molgraph"]) @@ -316,19 +320,23 @@ def filter_fragment_entries(self, fragment_entries): # If two entries are found that pass the above similarity check, take the one with the lower # energy: if entry["final_energy"] < filtered_entry["final_energy"]: - self.filtered_entries[ii] = entry + self.filtered_entries[idx] = entry # Note that this will essentially choose between singlet and triplet entries assuming both have # the same structural details break if not found_similar_entry: self.filtered_entries += [entry] - def build_new_entry(self, frags, bonds): + def build_new_entry(self, frags: list, bonds: list) -> list: """ - Simple function to format a bond dissociation entry that will eventually be returned to the user. + Build a new entry for bond dissociation that will be returned to the user. - :param frags: - :param bonds: + Args: + frags (list): Fragments involved in the bond dissociation. + bonds (list): Bonds broken in the dissociation process. + + Returns: + list: Formatted bond dissociation entries. """ specie = nx.get_node_attributes(self.mol_graph.graph, "specie") if len(frags) == 2: @@ -348,6 +356,7 @@ def build_new_entry(self, frags, bonds): frags[1]["initial_molecule"]["spin_multiplicity"], frags[1]["final_energy"], ] + else: new_entry = [ self.molecule_entry["final_energy"] - frags[0]["final_energy"], @@ -360,4 +369,5 @@ def build_new_entry(self, frags, bonds): frags[0]["initial_molecule"]["spin_multiplicity"], frags[0]["final_energy"], ] + return new_entry diff --git a/pymatgen/analysis/chemenv/connectivity/connectivity_finder.py b/pymatgen/analysis/chemenv/connectivity/connectivity_finder.py index 0c10a51d6ee..55c72970a95 100644 --- a/pymatgen/analysis/chemenv/connectivity/connectivity_finder.py +++ b/pymatgen/analysis/chemenv/connectivity/connectivity_finder.py @@ -24,9 +24,10 @@ def __init__(self, multiple_environments_choice=None): """ Constructor for the ConnectivityFinder. - :param multiple_environments_choice: defines the procedure to apply when - the environment of a given site is described as a "mix" of more than one - coordination environments. + Args: + multiple_environments_choice: defines the procedure to apply when + the environment of a given site is described as a "mix" of more than one + coordination environments. """ self.setup_parameters(multiple_environments_choice=multiple_environments_choice) @@ -35,8 +36,9 @@ def get_structure_connectivity(self, light_structure_environments): Get the structure connectivity from the coordination environments provided as an input. - :param light_structure_environments: LightStructureEnvironments with the - relevant coordination environments in the structure + Args: + light_structure_environments: LightStructureEnvironments with the + relevant coordination environments in the structure Returns: a StructureConnectivity object describing the connectivity of diff --git a/pymatgen/analysis/chemenv/coordination_environments/chemenv_strategies.py b/pymatgen/analysis/chemenv/coordination_environments/chemenv_strategies.py index 79edf04d466..d274f2cff37 100644 --- a/pymatgen/analysis/chemenv/coordination_environments/chemenv_strategies.py +++ b/pymatgen/analysis/chemenv/coordination_environments/chemenv_strategies.py @@ -9,7 +9,7 @@ import abc import os -from typing import ClassVar +from typing import TYPE_CHECKING, ClassVar import numpy as np from monty.json import MSONable @@ -30,6 +30,9 @@ from pymatgen.core.sites import PeriodicSite from pymatgen.symmetry.analyzer import SpacegroupAnalyzer +if TYPE_CHECKING: + from typing_extensions import Self + __author__ = "David Waroquiers" __copyright__ = "Copyright 2012, The Materials Project" __credits__ = "Geoffroy Hautier" @@ -62,7 +65,8 @@ class DistanceCutoffFloat(float, StrategyOption): def __new__(cls, cutoff): """Special float that should be between 1 and infinity. - :param cutoff: Distance cutoff. + Args: + cutoff: Distance cutoff. """ flt = float.__new__(cls, cutoff) if flt < 1: @@ -78,10 +82,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, d): + def from_dict(cls, d: dict) -> Self: """Initialize distance cutoff from dict. - :param d: Dict representation of the distance cutoff. + Args: + d: Dict representation of the distance cutoff. """ return cls(d["value"]) @@ -94,7 +99,8 @@ class AngleCutoffFloat(float, StrategyOption): def __new__(cls, cutoff): """Special float that should be between 0 and 1. - :param cutoff: Angle cutoff. + Args: + cutoff: Angle cutoff. """ flt = float.__new__(cls, cutoff) if not 0 <= flt <= 1: @@ -113,7 +119,8 @@ def as_dict(self): def from_dict(cls, d): """Initialize angle cutoff from dict. - :param d: Dict representation of the angle cutoff. + Args: + d: Dict representation of the angle cutoff. """ return cls(d["value"]) @@ -126,7 +133,8 @@ class CSMFloat(float, StrategyOption): def __new__(cls, cutoff): """Special float that should be between 0 and 100. - :param cutoff: CSM. + Args: + cutoff: CSM. """ flt = float.__new__(cls, cutoff) if not 0 <= flt <= 100: @@ -142,10 +150,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize CSM from dict. - :param d: Dict representation of the CSM. + Args: + d: Dict representation of the CSM. """ return cls(dct["value"]) @@ -175,10 +184,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize additional condition from dict. - :param d: Dict representation of the additional condition. + Args: + d: Dict representation of the additional condition. """ return cls(dct["value"]) @@ -202,8 +212,10 @@ def __init__( ): """ Abstract constructor for the all chemenv strategies. - :param structure_environments: StructureEnvironments object containing all the information on the - coordination of the sites in a structure. + + Args: + structure_environments: StructureEnvironments object containing all the information on the + coordination of the sites in a structure. """ self.structure_environments = None if structure_environments is not None: @@ -218,7 +230,8 @@ def symmetry_measure_type(self): def set_structure_environments(self, structure_environments): """Set the structure environments to this strategy. - :param structure_environments: StructureEnvironments object. + Args: + structure_environments: StructureEnvironments object. """ self.structure_environments = structure_environments if not isinstance(self.structure_environments.voronoi, DetailedVoronoiContainer): @@ -236,7 +249,8 @@ def prepare_symmetries(self): def equivalent_site_index_and_transform(self, psite): """Get the equivalent site and corresponding symmetry+translation transformations. - :param psite: Periodic site. + Args: + psite: Periodic site. Returns: Equivalent site in the unit cell, translations and symmetry transformation. @@ -305,9 +319,11 @@ def equivalent_site_index_and_transform(self, psite): def get_site_neighbors(self, site): """ Applies the strategy to the structure_environments object in order to get the neighbors of a given site. - :param site: Site for which the neighbors are looked for - :param structure_environments: StructureEnvironments object containing all the information needed to get the - neighbors of the site + + Args: + site: Site for which the neighbors are looked for + structure_environments: StructureEnvironments object containing all the information needed to get the + neighbors of the site Returns: The list of neighbors of the site. For complex strategies, where one allows multiple solutions, this @@ -325,7 +341,9 @@ def get_site_coordination_environment(self, site): """ Applies the strategy to the structure_environments object in order to define the coordination environment of a given site. - :param site: Site for which the coordination environment is looked for + + Args: + site: Site for which the coordination environment is looked for Returns: The coordination environment of the site. For complex strategies, where one allows multiple @@ -338,7 +356,9 @@ def get_site_coordination_environments(self, site): """ Applies the strategy to the structure_environments object in order to define the coordination environment of a given site. - :param site: Site for which the coordination environment is looked for + + Args: + site: Site for which the coordination environment is looked for Returns: The coordination environment of the site. For complex strategies, where one allows multiple @@ -362,7 +382,9 @@ def get_site_coordination_environments_fractions( """ Applies the strategy to the structure_environments object in order to define the coordination environment of a given site. - :param site: Site for which the coordination environment is looked for + + Args: + site: Site for which the coordination environment is looked for Returns: The coordination environment of the site. For complex strategies, where one allows multiple @@ -374,7 +396,9 @@ def get_site_ce_fractions_and_neighbors(self, site, full_ce_info=False, strategy """ Applies the strategy to the structure_environments object in order to get coordination environments, their fraction, csm, geometry_info, and neighbors - :param site: Site for which the above information is sought + + Args: + site: Site for which the above information is sought Returns: The list of neighbors of the site. For complex strategies, where one allows multiple solutions, this @@ -408,15 +432,17 @@ def get_site_ce_fractions_and_neighbors(self, site, full_ce_info=False, strategy def set_option(self, option_name, option_value): """Set up a given option for this strategy. - :param option_name: Name of the option. - :param option_value: Value for this option. + Args: + option_name: Name of the option. + option_value: Value for this option. """ setattr(self, option_name, option_value) def setup_options(self, all_options_dict): """Set up options for this strategy based on a dict. - :param all_options_dict: Dict of option_name->option_value. + Args: + all_options_dict: Dict of option_name->option_value. """ for option_name, option_value in all_options_dict.items(): self.set_option(option_name, option_value) @@ -425,7 +451,9 @@ def setup_options(self, all_options_dict): def __eq__(self, other: object) -> bool: """ Equality method that should be implemented for any strategy - :param other: strategy to be compared with the current one + + Args: + other: strategy to be compared with the current one """ raise NotImplementedError @@ -455,7 +483,9 @@ def from_dict(cls, dct) -> AbstractChemenvStrategy: """ Reconstructs the SimpleAbundanceChemenvStrategy object from a dict representation of the SimpleAbundanceChemenvStrategy object created using the as_dict method. - :param dct: dict representation of the SimpleAbundanceChemenvStrategy object + + Args: + dct: dict representation of the SimpleAbundanceChemenvStrategy object Returns: StructureEnvironments object. @@ -498,10 +528,10 @@ class SimplestChemenvStrategy(AbstractChemenvStrategy): ) STRATEGY_DESCRIPTION = ( - " Simplest ChemenvStrategy using fixed angle and distance parameters \n" - " for the definition of neighbors in the Voronoi approach. \n" - " The coordination environment is then given as the one with the \n" - " lowest continuous symmetry measure." + "Simplest ChemenvStrategy using fixed angle and distance parameters \n" + "for the definition of neighbors in the Voronoi approach. \n" + "The coordination environment is then given as the one with the \n" + "lowest continuous symmetry measure." ) def __init__( @@ -515,8 +545,10 @@ def __init__( ): """ Constructor for this SimplestChemenvStrategy. - :param distance_cutoff: Distance cutoff used - :param angle_cutoff: Angle cutoff used. + + Args: + distance_cutoff: Distance cutoff used + angle_cutoff: Angle cutoff used. """ AbstractChemenvStrategy.__init__(self, structure_environments, symmetry_measure_type=symmetry_measure_type) self.distance_cutoff = distance_cutoff @@ -538,7 +570,8 @@ def distance_cutoff(self): def distance_cutoff(self, distance_cutoff): """Set the distance cutoff for this strategy. - :param distance_cutoff: Distance cutoff. + Args: + distance_cutoff: Distance cutoff. """ self._distance_cutoff = DistanceCutoffFloat(distance_cutoff) @@ -551,7 +584,8 @@ def angle_cutoff(self): def angle_cutoff(self, angle_cutoff): """Set the angle cutoff for this strategy. - :param angle_cutoff: Angle cutoff. + Args: + angle_cutoff: Angle cutoff. """ self._angle_cutoff = AngleCutoffFloat(angle_cutoff) @@ -564,7 +598,8 @@ def additional_condition(self): def additional_condition(self, additional_condition): """Set the additional condition for this strategy. - :param additional_condition: Additional condition. + Args: + additional_condition: Additional condition. """ self._additional_condition = AdditionalConditionInt(additional_condition) @@ -577,18 +612,20 @@ def continuous_symmetry_measure_cutoff(self): def continuous_symmetry_measure_cutoff(self, continuous_symmetry_measure_cutoff): """Set the CSM cutoff for this strategy. - :param continuous_symmetry_measure_cutoff: CSM cutoff + Args: + continuous_symmetry_measure_cutoff: CSM cutoff """ self._continuous_symmetry_measure_cutoff = CSMFloat(continuous_symmetry_measure_cutoff) def get_site_neighbors(self, site, isite=None, dequivsite=None, dthissite=None, mysym=None): """Get the neighbors of a given site. - :param site: Site for which neighbors are needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. + Args: + site: Site for which neighbors are needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. Returns: List of coordinated neighbors of site. @@ -626,12 +663,13 @@ def get_site_coordination_environment( ): """Get the coordination environment of a given site. - :param site: Site for which coordination environment is needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. - :param return_map: Whether to return cn_map (identifies the NeighborsSet used). + Args: + site: Site for which coordination environment is needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. + return_map: Whether to return cn_map (identifies the NeighborsSet used). Returns: Coordination environment of site. @@ -708,15 +746,16 @@ def get_site_coordination_environments_fractions( ): """Get the coordination environments of a given site and additional information. - :param site: Site for which coordination environment is needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. - :param ordered: Whether to order the list by fractions. - :param min_fraction: Minimum fraction to include in the list - :param return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). - :param return_strategy_dict_info: Whether to add the info about the strategy used. + Args: + site: Site for which coordination environment is needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. + ordered: Whether to order the list by fractions. + min_fraction: Minimum fraction to include in the list + return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). + return_strategy_dict_info: Whether to add the info about the strategy used. Returns: List of Dict with coordination environment, fraction and additional info. @@ -762,12 +801,13 @@ def get_site_coordination_environments( ): """Get the coordination environments of a given site. - :param site: Site for which coordination environment is needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. - :param return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). + Args: + site: Site for which coordination environment is needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. + return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). Returns: List of coordination environment. @@ -780,9 +820,10 @@ def get_site_coordination_environments( def add_strategy_visualization_to_subplot(self, subplot, visualization_options=None, plot_type=None): """Add a visual of the strategy on a distance-angle plot. - :param subplot: Axes object onto the visual should be added. - :param visualization_options: Options for the visual. - :param plot_type: Type of distance-angle plot. + Args: + subplot: Axes object onto the visual should be added. + visualization_options: Options for the visual. + plot_type: Type of distance-angle plot. """ subplot.plot( self._distance_cutoff, self._angle_cutoff, "o", markeredgecolor=None, markerfacecolor="w", markersize=12 @@ -823,7 +864,9 @@ def from_dict(cls, dct: dict) -> SimplestChemenvStrategy: """ Reconstructs the SimplestChemenvStrategy object from a dict representation of the SimplestChemenvStrategy object created using the as_dict method. - :param dct: dict representation of the SimplestChemenvStrategy object + + Args: + dct: dict representation of the SimplestChemenvStrategy object Returns: StructureEnvironments object. @@ -855,10 +898,10 @@ class SimpleAbundanceChemenvStrategy(AbstractChemenvStrategy): ), ) STRATEGY_DESCRIPTION = ( - ' Simple Abundance ChemenvStrategy using the most "abundant" neighbors map \n' - " for the definition of neighbors in the Voronoi approach. \n" - " The coordination environment is then given as the one with the \n" - " lowest continuous symmetry measure." + 'Simple Abundance ChemenvStrategy using the most "abundant" neighbors map \n' + "for the definition of neighbors in the Voronoi approach. \n" + "The coordination environment is then given as the one with the \n" + "lowest continuous symmetry measure." ) def __init__( @@ -869,8 +912,10 @@ def __init__( ): """ Constructor for the SimpleAbundanceChemenvStrategy. - :param structure_environments: StructureEnvironments object containing all the information on the - coordination of the sites in a structure. + + Args: + structure_environments: StructureEnvironments object containing all the information on the + coordination of the sites in a structure. """ raise NotImplementedError("SimpleAbundanceChemenvStrategy not yet implemented") AbstractChemenvStrategy.__init__(self, structure_environments, symmetry_measure_type=symmetry_measure_type) @@ -884,7 +929,8 @@ def uniquely_determines_coordination_environments(self): def get_site_neighbors(self, site): """Get the neighbors of a given site with this strategy. - :param site: Periodic site. + Args: + site: Periodic site. Returns: List of neighbors of site. @@ -910,12 +956,13 @@ def get_site_coordination_environment( ): """Get the coordination environment of a given site. - :param site: Site for which coordination environment is needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. - :param return_map: Whether to return cn_map (identifies the NeighborsSet used). + Args: + site: Site for which coordination environment is needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. + return_map: Whether to return cn_map (identifies the NeighborsSet used). Returns: Coordination environment of site. @@ -951,12 +998,13 @@ def get_site_coordination_environments( ): """Get the coordination environments of a given site. - :param site: Site for which coordination environment is needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. - :param return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). + Args: + site: Site for which coordination environment is needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. + return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). Returns: List of coordination environment. @@ -1021,7 +1069,9 @@ def from_dict(cls, dct: dict) -> SimpleAbundanceChemenvStrategy: """ Reconstructs the SimpleAbundanceChemenvStrategy object from a dict representation of the SimpleAbundanceChemenvStrategy object created using the as_dict method. - :param dct: dict representation of the SimpleAbundanceChemenvStrategy object + + Args: + dct: dict representation of the SimpleAbundanceChemenvStrategy object Returns: StructureEnvironments object. @@ -1053,15 +1103,20 @@ def __init__( """Initialize strategy. Not yet implemented. - :param structure_environments: - :param truncate_dist_ang: - :param additional_condition: - :param max_nabundant: - :param target_environments: - :param target_penalty_type: - :param max_csm: - :param symmetry_measure_type: + + Args: + structure_environments: + truncate_dist_ang: + additional_condition: + max_nabundant: + target_environments: + target_penalty_type: + max_csm: + symmetry_measure_type: """ + + raise NotImplementedError("TargetedPenaltiedAbundanceChemenvStrategy not yet implemented") + super().__init__( self, structure_environments, @@ -1072,7 +1127,6 @@ def __init__( self.target_environments = target_environments self.target_penalty_type = target_penalty_type self.max_csm = max_csm - raise NotImplementedError("TargetedPenaltiedAbundanceChemenvStrategy not yet implemented") def get_site_coordination_environment( self, @@ -1085,12 +1139,13 @@ def get_site_coordination_environment( ): """Get the coordination environment of a given site. - :param site: Site for which coordination environment is needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. - :param return_map: Whether to return cn_map (identifies the NeighborsSet used). + Args: + site: Site for which coordination environment is needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. + return_map: Whether to return cn_map (identifies the NeighborsSet used). Returns: Coordination environment of site. @@ -1172,7 +1227,7 @@ def as_dict(self): "max_csm": self.max_csm, } - def __eq__(self, other: object) -> bool: + def __eq__(self, other): if not isinstance(other, type(self)): return NotImplemented @@ -1189,7 +1244,9 @@ def from_dict(cls, dct) -> TargetedPenaltiedAbundanceChemenvStrategy: """ Reconstructs the TargetedPenaltiedAbundanceChemenvStrategy object from a dict representation of the TargetedPenaltiedAbundanceChemenvStrategy object created using the as_dict method. - :param dct: dict representation of the TargetedPenaltiedAbundanceChemenvStrategy object + + Args: + dct: dict representation of the TargetedPenaltiedAbundanceChemenvStrategy object Returns: TargetedPenaltiedAbundanceChemenvStrategy object. @@ -1214,10 +1271,11 @@ def as_dict(self): def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -1232,7 +1290,8 @@ class AngleNbSetWeight(NbSetWeight): def __init__(self, aa=1): """Initialize AngleNbSetWeight estimator. - :param aa: Exponent of the angle for the estimator. + Args: + aa: Exponent of the angle for the estimator. """ self.aa = aa if self.aa == 1: @@ -1243,10 +1302,11 @@ def __init__(self, aa=1): def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -1257,7 +1317,8 @@ def weight(self, nb_set, structure_environments, cn_map=None, additional_info=No def angle_sum(nb_set): """Sum of all angles in a neighbors set. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: Sum of solid angles for the neighbors set. @@ -1267,7 +1328,8 @@ def angle_sum(nb_set): def angle_sumn(self, nb_set): """Sum of all angles to a given power in a neighbors set. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: Sum of solid angles to the power aa for the neighbors set. @@ -1301,9 +1363,10 @@ class NormalizedAngleDistanceNbSetWeight(NbSetWeight): def __init__(self, average_type, aa, bb): """Initialize NormalizedAngleDistanceNbSetWeight. - :param average_type: Average function. - :param aa: Exponent for the angle values. - :param bb: Exponent for the distance values. + Args: + average_type: Average function. + aa: Exponent for the angle values. + bb: Exponent for the distance values. """ self.average_type = average_type if self.average_type == "geometric": @@ -1351,10 +1414,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of NormalizedAngleDistanceNbSetWeight. + Args: + dct: Dict representation of NormalizedAngleDistanceNbSetWeight. Returns: NormalizedAngleDistanceNbSetWeight. @@ -1365,7 +1429,8 @@ def from_dict(cls, dct): def invdist(nb_set): """Inverse distance weight. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: List of inverse distances. @@ -1375,7 +1440,8 @@ def invdist(nb_set): def invndist(self, nb_set): """Inverse power distance weight. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: List of inverse power distances. @@ -1386,7 +1452,8 @@ def invndist(self, nb_set): def ang(nb_set): """Angle weight. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: List of angle weights. @@ -1396,7 +1463,8 @@ def ang(nb_set): def angn(self, nb_set): """Power angle weight. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: List of power angle weights. @@ -1407,7 +1475,8 @@ def angn(self, nb_set): def anginvdist(nb_set): """Angle/distance weight. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: List of angle/distance weights. @@ -1418,7 +1487,8 @@ def anginvdist(nb_set): def anginvndist(self, nb_set): """Angle/power distance weight. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: List of angle/power distance weights. @@ -1429,7 +1499,8 @@ def anginvndist(self, nb_set): def angninvdist(self, nb_set): """Power angle/distance weight. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: List of power angle/distance weights. @@ -1440,7 +1511,8 @@ def angninvdist(self, nb_set): def angninvndist(self, nb_set): """Power angle/power distance weight. - :param nb_set: Neighbors set. + Args: + nb_set: Neighbors set. Returns: List of power angle/power distance weights. @@ -1451,10 +1523,11 @@ def angninvndist(self, nb_set): def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -1466,7 +1539,8 @@ def weight(self, nb_set, structure_environments, cn_map=None, additional_info=No def gweight(fda_list): """Geometric mean of the weights. - :param fda_list: List of estimator weights for each neighbor. + Args: + fda_list: List of estimator weights for each neighbor. Returns: Geometric mean of the weights. @@ -1477,7 +1551,8 @@ def gweight(fda_list): def aweight(fda_list): """Standard mean of the weights. - :param fda_list: List of estimator weights for each neighbor. + Args: + fda_list: List of estimator weights for each neighbor. Returns: Standard mean of the weights. @@ -1496,13 +1571,14 @@ def get_effective_csm( ): """Get the effective continuous symmetry measure of a given neighbors set. - :param nb_set: Neighbors set. - :param cn_map: Mapping index of this neighbors set. - :param structure_environments: Structure environments. - :param additional_info: Additional information for the neighbors set. - :param symmetry_measure_type: Type of symmetry measure to be used in the effective CSM. - :param max_effective_csm: Max CSM to use for the effective CSM calculation. - :param effective_csm_estimator_ratio_function: Ratio function to use to compute effective CSM. + Args: + nb_set: Neighbors set. + cn_map: Mapping index of this neighbors set. + structure_environments: Structure environments. + additional_info: Additional information for the neighbors set. + symmetry_measure_type: Type of symmetry measure to be used in the effective CSM. + max_effective_csm: Max CSM to use for the effective CSM calculation. + effective_csm_estimator_ratio_function: Ratio function to use to compute effective CSM. Returns: Effective CSM of a given Neighbors set. """ @@ -1536,16 +1612,15 @@ def get_effective_csm( return effective_csm -def set_info(additional_info, field, isite, cn_map, value): +def set_info(additional_info, field, isite, cn_map, value) -> None: """Set additional information for the weights. - :param additional_info: Additional information. - :param field: Type of additional information. - :param isite: Index of site to add info. - :param cn_map: Mapping index of the neighbors set. - :param value: Value of this additional information. - Returns: - None + Args: + additional_info: Additional information. + field: Type of additional information. + isite: Index of site to add info. + cn_map: Mapping index of the neighbors set. + value: Value of this additional information. """ try: additional_info[field][isite][cn_map] = value @@ -1579,9 +1654,10 @@ def __init__( ): """Initialize SelfCSMNbSetWeight. - :param effective_csm_estimator: Ratio function used for the effective CSM (comparison between neighbors sets). - :param weight_estimator: Weight estimator within a given neighbors set. - :param symmetry_measure_type: Type of symmetry measure to be used. + Args: + effective_csm_estimator: Ratio function used for the effective CSM (comparison between neighbors sets). + weight_estimator: Weight estimator within a given neighbors set. + symmetry_measure_type: Type of symmetry measure to be used. """ self.effective_csm_estimator = effective_csm_estimator self.effective_csm_estimator_rf = CSMInfiniteRatioFunction.from_dict(effective_csm_estimator) @@ -1593,10 +1669,11 @@ def __init__( def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -1641,10 +1718,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of SelfCSMNbSetWeight. + Args: + dct: Dict representation of SelfCSMNbSetWeight. Returns: SelfCSMNbSetWeight. @@ -1680,10 +1758,11 @@ def __init__( ): """Initialize DeltaCSMNbSetWeight. - :param effective_csm_estimator: Ratio function used for the effective CSM (comparison between neighbors sets). - :param weight_estimator: Weight estimator within a given neighbors set. - :param delta_cn_weight_estimators: Specific weight estimators for specific cn - :param symmetry_measure_type: Type of symmetry measure to be used. + Args: + effective_csm_estimator: Ratio function used for the effective CSM (comparison between neighbors sets). + weight_estimator: Weight estimator within a given neighbors set. + delta_cn_weight_estimators: Specific weight estimators for specific cn + symmetry_measure_type: Type of symmetry measure to be used. """ self.effective_csm_estimator = effective_csm_estimator self.effective_csm_estimator_rf = CSMInfiniteRatioFunction.from_dict(effective_csm_estimator) @@ -1701,10 +1780,11 @@ def __init__( def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -1817,11 +1897,12 @@ def delta_cn_specifics( ): """Initialize DeltaCSMNbSetWeight from specific coordination number differences. - :param delta_csm_mins: Minimums for each coordination number. - :param delta_csm_maxs: Maximums for each coordination number. - :param function: Ratio function used. - :param symmetry_measure_type: Type of symmetry measure to be used. - :param effective_csm_estimator: Ratio function used for the effective CSM (comparison between neighbors sets). + Args: + delta_csm_mins: Minimums for each coordination number. + delta_csm_maxs: Maximums for each coordination number. + function: Ratio function used. + symmetry_measure_type: Type of symmetry measure to be used. + effective_csm_estimator: Ratio function used for the effective CSM (comparison between neighbors sets). Returns: DeltaCSMNbSetWeight. @@ -1875,10 +1956,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of DeltaCSMNbSetWeight. + Args: + dct: Dict representation of DeltaCSMNbSetWeight. Returns: DeltaCSMNbSetWeight. @@ -1903,8 +1985,9 @@ class CNBiasNbSetWeight(NbSetWeight): def __init__(self, cn_weights, initialization_options): """Initialize CNBiasNbSetWeight. - :param cn_weights: Weights for each coordination. - :param initialization_options: Options for initialization. + Args: + cn_weights: Weights for each coordination. + initialization_options: Options for initialization. """ self.cn_weights = cn_weights self.initialization_options = initialization_options @@ -1912,10 +1995,11 @@ def __init__(self, cn_weights, initialization_options): def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -1938,10 +2022,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of CNBiasNbSetWeight. + Args: + dct: Dict representation of CNBiasNbSetWeight. Returns: CNBiasNbSetWeight. @@ -1955,8 +2040,9 @@ def from_dict(cls, dct): def linearly_equidistant(cls, weight_cn1, weight_cn13): """Initialize linearly equidistant weights for each coordination. - :param weight_cn1: Weight of coordination 1. - :param weight_cn13: Weight of coordination 13. + Args: + weight_cn1: Weight of coordination 1. + weight_cn13: Weight of coordination 13. Returns: CNBiasNbSetWeight. @@ -1974,8 +2060,9 @@ def linearly_equidistant(cls, weight_cn1, weight_cn13): def geometrically_equidistant(cls, weight_cn1, weight_cn13): """Initialize geometrically equidistant weights for each coordination. - :param weight_cn1: Weight of coordination 1. - :param weight_cn13: Weight of coordination 13. + Arge: + weight_cn1: Weight of coordination 1. + weight_cn13: Weight of coordination 13. Returns: CNBiasNbSetWeight. @@ -1993,7 +2080,8 @@ def geometrically_equidistant(cls, weight_cn1, weight_cn13): def explicit(cls, cn_weights): """Initialize weights explicitly for each coordination. - :param cn_weights: Weights for each coordination. + Args: + cn_weights: Weights for each coordination. Returns: CNBiasNbSetWeight. @@ -2004,10 +2092,11 @@ def explicit(cls, cn_weights): return cls(cn_weights=cn_weights, initialization_options=initialization_options) @classmethod - def from_description(cls, dct): + def from_description(cls, dct: dict) -> Self: """Initialize weights from description. - :param dct: Dictionary description. + Args: + dct: Dictionary description. Returns: CNBiasNbSetWeight. @@ -2018,7 +2107,8 @@ def from_description(cls, dct): return cls.geometrically_equidistant(weight_cn1=dct["weight_cn1"], weight_cn13=dct["weight_cn13"]) if dct["type"] == "explicit": return cls.explicit(cn_weights=dct["cn_weights"]) - return None + + raise RuntimeError("Cannot initialize Weights.") class DistanceAngleAreaNbSetWeight(NbSetWeight): @@ -2045,13 +2135,14 @@ def __init__( ): """Initialize CNBiasNbSetWeight. - :param weight_type: Type of weight. - :param surface_definition: Definition of the surface. - :param nb_sets_from_hints: How to deal with neighbors sets obtained from "hints". - :param other_nb_sets: What to do with other neighbors sets. - :param additional_condition: Additional condition to be used. - :param smoothstep_distance: Smoothstep distance. - :param smoothstep_angle: Smoothstep angle. + Args: + weight_type: Type of weight. + surface_definition: Definition of the surface. + nb_sets_from_hints: How to deal with neighbors sets obtained from "hints". + other_nb_sets: What to do with other neighbors sets. + additional_condition: Additional condition to be used. + smoothstep_distance: Smoothstep distance. + smoothstep_angle: Smoothstep angle. """ self.weight_type = weight_type if weight_type == "has_intersection": @@ -2084,10 +2175,11 @@ def __init__( def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -2102,10 +2194,11 @@ def weight(self, nb_set, structure_environments, cn_map=None, additional_info=No def w_area_has_intersection(self, nb_set, structure_environments, cn_map, additional_info): """Get intersection of the neighbors set area with the surface. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments. - :param cn_map: Mapping index of the neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments. + cn_map: Mapping index of the neighbors set. + additional_info: Additional information. Returns: Area intersection between neighbors set and surface. @@ -2120,10 +2213,11 @@ def w_area_has_intersection(self, nb_set, structure_environments, cn_map, additi def w_area_intersection_nbsfh_fbs_onb0(self, nb_set, structure_environments, cn_map, additional_info): """Get intersection of the neighbors set area with the surface. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments. - :param cn_map: Mapping index of the neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments. + cn_map: Mapping index of the neighbors set. + additional_info: Additional information. Returns: Area intersection between neighbors set and surface. @@ -2169,10 +2263,11 @@ def w_area_intersection_nbsfh_fbs_onb0(self, nb_set, structure_environments, cn_ def rectangle_crosses_area(self, d1, d2, a1, a2): """Whether a given rectangle crosses the area defined by the upper and lower curves. - :param d1: lower d. - :param d2: upper d. - :param a1: lower a. - :param a2: upper a. + Args: + d1: lower d. + d2: upper d. + a1: lower a. + a2: upper a. """ # Case 1 if d1 <= self.dmin and d2 <= self.dmin: @@ -2235,10 +2330,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of DistanceAngleAreaNbSetWeight. + Args: + dct: Dict representation of DistanceAngleAreaNbSetWeight. Returns: DistanceAngleAreaNbSetWeight. @@ -2260,8 +2356,9 @@ class DistancePlateauNbSetWeight(NbSetWeight): def __init__(self, distance_function=None, weight_function=None): """Initialize DistancePlateauNbSetWeight. - :param distance_function: Distance function to use. - :param weight_function: Ratio function to use. + Args: + distance_function: Distance function to use. + weight_function: Ratio function to use. """ if distance_function is None: self.distance_function = {"type": "normalized_distance"} @@ -2279,10 +2376,11 @@ def __init__(self, distance_function=None, weight_function=None): def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -2302,10 +2400,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of DistancePlateauNbSetWeight. + Args: + dct: Dict representation of DistancePlateauNbSetWeight. Returns: DistancePlateauNbSetWeight. @@ -2324,8 +2423,9 @@ class AnglePlateauNbSetWeight(NbSetWeight): def __init__(self, angle_function=None, weight_function=None): """Initialize AnglePlateauNbSetWeight. - :param angle_function: Angle function to use. - :param weight_function: Ratio function to use. + Args: + angle_function: Angle function to use. + weight_function: Ratio function to use. """ if angle_function is None: self.angle_function = {"type": "normalized_angle"} @@ -2343,10 +2443,11 @@ def __init__(self, angle_function=None, weight_function=None): def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -2366,10 +2467,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of AnglePlateauNbSetWeight. + Args: + dct: Dict representation of AnglePlateauNbSetWeight. Returns: AnglePlateauNbSetWeight. @@ -2385,8 +2487,9 @@ class DistanceNbSetWeight(NbSetWeight): def __init__(self, weight_function=None, nbs_source="voronoi"): """Initialize DistanceNbSetWeight. - :param weight_function: Ratio function to use. - :param nbs_source: Source of the neighbors. + Args: + weight_function: Ratio function to use. + nbs_source: Source of the neighbors. """ if weight_function is None: self.weight_function = { @@ -2403,10 +2506,11 @@ def __init__(self, weight_function=None, nbs_source="voronoi"): def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -2444,10 +2548,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of DistanceNbSetWeight. + Args: + dct: Dict representation of DistanceNbSetWeight. Returns: DistanceNbSetWeight. @@ -2463,8 +2568,9 @@ class DeltaDistanceNbSetWeight(NbSetWeight): def __init__(self, weight_function=None, nbs_source="voronoi"): """Initialize DeltaDistanceNbSetWeight. - :param weight_function: Ratio function to use. - :param nbs_source: Source of the neighbors. + Args: + weight_function: Ratio function to use. + nbs_source: Source of the neighbors. """ if weight_function is None: self.weight_function = { @@ -2481,10 +2587,11 @@ def __init__(self, weight_function=None, nbs_source="voronoi"): def weight(self, nb_set, structure_environments, cn_map=None, additional_info=None): """Get the weight of a given neighbors set. - :param nb_set: Neighbors set. - :param structure_environments: Structure environments used to estimate weight. - :param cn_map: Mapping index for this neighbors set. - :param additional_info: Additional information. + Args: + nb_set: Neighbors set. + structure_environments: Structure environments used to estimate weight. + cn_map: Mapping index for this neighbors set. + additional_info: Additional information. Returns: Weight of the neighbors set. @@ -2525,10 +2632,11 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Initialize from dict. - :param dct: Dict representation of DeltaDistanceNbSetWeight. + Args: + dct: Dict representation of DeltaDistanceNbSetWeight. Returns: DeltaDistanceNbSetWeight. @@ -2555,8 +2663,10 @@ def __init__( ): """ Constructor for the WeightedNbSetChemenvStrategy. - :param structure_environments: StructureEnvironments object containing all the information on the - coordination of the sites in a structure. + + Args: + structure_environments: StructureEnvironments object containing all the information on the + coordination of the sites in a structure. """ if nb_set_weights is None: raise ValueError(f"{nb_set_weights=} must be provided") @@ -2590,15 +2700,16 @@ def get_site_coordination_environments_fractions( ): """Get the coordination environments of a given site and additional information. - :param site: Site for which coordination environment is needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. - :param ordered: Whether to order the list by fractions. - :param min_fraction: Minimum fraction to include in the list - :param return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). - :param return_strategy_dict_info: Whether to add the info about the strategy used. + Args: + site: Site for which coordination environment is needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. + ordered: Whether to order the list by fractions. + min_fraction: Minimum fraction to include in the list + return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). + return_strategy_dict_info: Whether to add the info about the strategy used. Returns: List of Dict with coordination environment, fraction and additional info. @@ -2761,12 +2872,13 @@ def get_site_coordination_environments( ): """Get the coordination environments of a given site. - :param site: Site for which coordination environment is needed. - :param isite: Index of the site. - :param dequivsite: Translation of the equivalent site. - :param dthissite: Translation of this site. - :param mysym: Symmetry to be applied. - :param return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). + Args: + site: Site for which coordination environment is needed. + isite: Index of the site. + dequivsite: Translation of the equivalent site. + dthissite: Translation of this site. + mysym: Symmetry to be applied. + return_maps: Whether to return cn_maps (identifies all the NeighborsSet used). Returns: List of coordination environment. @@ -2812,11 +2924,13 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct) -> WeightedNbSetChemenvStrategy: + def from_dict(cls, dct: dict) -> WeightedNbSetChemenvStrategy: """ Reconstructs the WeightedNbSetChemenvStrategy object from a dict representation of the WeightedNbSetChemenvStrategy object created using the as_dict method. - :param dct: dict representation of the WeightedNbSetChemenvStrategy object + + Args: + dct: dict representation of the WeightedNbSetChemenvStrategy object Returns: WeightedNbSetChemenvStrategy object. @@ -2832,7 +2946,7 @@ def from_dict(cls, dct) -> WeightedNbSetChemenvStrategy: class MultiWeightsChemenvStrategy(WeightedNbSetChemenvStrategy): """MultiWeightsChemenvStrategy.""" - STRATEGY_DESCRIPTION = " Multi Weights ChemenvStrategy" + STRATEGY_DESCRIPTION = "Multi Weights ChemenvStrategy" # STRATEGY_INFO_FIELDS = ['cn_map_surface_fraction', 'cn_map_surface_weight', # 'cn_map_mean_csm', 'cn_map_csm_weight', # 'cn_map_delta_csm', 'cn_map_delta_csms_cn_map2', 'cn_map_delta_csm_weight', @@ -2858,8 +2972,10 @@ def __init__( ): """ Constructor for the MultiWeightsChemenvStrategy. - :param structure_environments: StructureEnvironments object containing all the information on the - coordination of the sites in a structure. + + Args: + structure_environments: StructureEnvironments object containing all the information on the + coordination of the sites in a structure. """ self._additional_condition = additional_condition self.dist_ang_area_weight = dist_ang_area_weight @@ -2985,11 +3101,13 @@ def as_dict(self): } @classmethod - def from_dict(cls, dct) -> MultiWeightsChemenvStrategy: + def from_dict(cls, dct: dict) -> MultiWeightsChemenvStrategy: """ Reconstructs the MultiWeightsChemenvStrategy object from a dict representation of the MultipleAbundanceChemenvStrategy object created using the as_dict method. - :param dct: dict representation of the MultiWeightsChemenvStrategy object + + Args: + dct: dict representation of the MultiWeightsChemenvStrategy object Returns: MultiWeightsChemenvStrategy object. diff --git a/pymatgen/analysis/chemenv/coordination_environments/coordination_geometry_finder.py b/pymatgen/analysis/chemenv/coordination_environments/coordination_geometry_finder.py index 165978b3f97..42bc3c2f305 100644 --- a/pymatgen/analysis/chemenv/coordination_environments/coordination_geometry_finder.py +++ b/pymatgen/analysis/chemenv/coordination_environments/coordination_geometry_finder.py @@ -18,6 +18,7 @@ import logging import time from random import shuffle +from typing import TYPE_CHECKING import numpy as np from numpy.linalg import norm, svd @@ -46,6 +47,9 @@ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.util.due import Doi, due +if TYPE_CHECKING: + from typing_extensions import Self + __author__ = "David Waroquiers" __copyright__ = "Copyright 2012, The Materials Project" __credits__ = "Geoffroy Hautier" @@ -80,12 +84,16 @@ def __init__( ): """ Constructor for the abstract geometry - :param central_site: Coordinates of the central site - :param bare_coords: Coordinates of the neighbors of the central site - :param centering_type: How to center the abstract geometry - :param include_central_site_in_centroid: When the centering is on the centroid, the central site is included - if this parameter is set to True. - :raise: ValueError if the parameters are not consistent. + + Args: + central_site: Coordinates of the central site + bare_coords: Coordinates of the neighbors of the central site + centering_type: How to center the abstract geometry + include_central_site_in_centroid: When the centering is on the centroid, + the central site is included if this parameter is set to True. + + Raises: + ValueError if the parameters are not consistent. """ bcoords = np.array(bare_coords) self.bare_centre = np.array(central_site) @@ -185,11 +193,12 @@ def __str__(self): return "\n".join(outs) @classmethod - def from_cg(cls, cg, centering_type="standard", include_central_site_in_centroid=False): + def from_cg(cls, cg, centering_type="standard", include_central_site_in_centroid=False) -> Self: """ - :param cg: - :param centering_type: - :param include_central_site_in_centroid: + Args: + cg: + centering_type: + include_central_site_in_centroid: """ central_site = cg.get_central_site() bare_coords = [np.array(pt, float) for pt in cg.points] @@ -202,7 +211,8 @@ def from_cg(cls, cg, centering_type="standard", include_central_site_in_centroid def points_wcs_csc(self, permutation=None): """ - :param permutation: + Args: + permutation: """ if permutation is None: return self._points_wcs_csc @@ -210,7 +220,8 @@ def points_wcs_csc(self, permutation=None): def points_wocs_csc(self, permutation=None): """ - :param permutation: + Args: + permutation: """ if permutation is None: return self._points_wocs_csc @@ -218,7 +229,8 @@ def points_wocs_csc(self, permutation=None): def points_wcs_ctwcc(self, permutation=None): """ - :param permutation: + Args: + permutation: """ if permutation is None: return self._points_wcs_ctwcc @@ -231,7 +243,8 @@ def points_wcs_ctwcc(self, permutation=None): def points_wocs_ctwcc(self, permutation=None): """ - :param permutation: + Args: + permutation: """ if permutation is None: return self._points_wocs_ctwcc @@ -239,7 +252,8 @@ def points_wocs_ctwcc(self, permutation=None): def points_wcs_ctwocc(self, permutation=None): """ - :param permutation: + Args: + permutation: """ if permutation is None: return self._points_wcs_ctwocc @@ -252,7 +266,8 @@ def points_wcs_ctwocc(self, permutation=None): def points_wocs_ctwocc(self, permutation=None): """ - :param permutation: + Args: + permutation: """ if permutation is None: return self._points_wocs_ctwocc @@ -273,10 +288,13 @@ def symmetry_measure(points_distorted, points_perfect): """ Computes the continuous symmetry measure of the (distorted) set of points "points_distorted" with respect to the (perfect) set of points "points_perfect". - :param points_distorted: List of points describing a given (distorted) polyhedron for which the symmetry measure - has to be computed with respect to the model polyhedron described by the list of points - "points_perfect". - :param points_perfect: List of "perfect" points describing a given model polyhedron. + + Args: + points_distorted: List of points describing a given (distorted) polyhedron for which the symmetry measure + has to be computed with respect to the model polyhedron described by the list of points + "points_perfect". + points_perfect: List of "perfect" points describing a given model polyhedron. + Returns: The continuous symmetry measure of the distorted polyhedron with respect to the perfect polyhedron. """ @@ -306,9 +324,12 @@ def find_rotation(points_distorted, points_perfect): """ This finds the rotation matrix that aligns the (distorted) set of points "points_distorted" with respect to the (perfect) set of points "points_perfect" in a least-square sense. - :param points_distorted: List of points describing a given (distorted) polyhedron for which the rotation that - aligns these points in a least-square sense to the set of perfect points "points_perfect" - :param points_perfect: List of "perfect" points describing a given model polyhedron. + + Args: + points_distorted: List of points describing a given (distorted) polyhedron for which the rotation that + aligns these points in a least-square sense to the set of perfect points "points_perfect" + points_perfect: List of "perfect" points describing a given model polyhedron. + Returns: The rotation matrix. """ @@ -321,10 +342,12 @@ def find_scaling_factor(points_distorted, points_perfect, rot): """ This finds the scaling factor between the (distorted) set of points "points_distorted" and the (perfect) set of points "points_perfect" in a least-square sense. - :param points_distorted: List of points describing a given (distorted) polyhedron for which the scaling factor has - to be obtained. - :param points_perfect: List of "perfect" points describing a given model polyhedron. - :param rot: The rotation matrix + + Args: + points_distorted: List of points describing a given (distorted) polyhedron for + which the scaling factor has to be obtained. + points_perfect: List of "perfect" points describing a given model polyhedron. + rot: The rotation matrix Returns: The scaling factor between the two structures and the rotated set of (distorted) points. @@ -403,15 +426,17 @@ def setup_parameters( chosen. This can be the centroid of the structure (including or excluding the atom for which the coordination geometry is looked for) or the atom itself. In the 'standard' centering_type, the reference point is the central atom for coordination numbers 1, 2, 3 and 4 and the centroid for coordination numbers > 4. - :param centering_type: Type of the reference point (centering) 'standard', 'centroid' or 'central_site' - :param include_central_site_in_centroid: In case centering_type is 'centroid', the central site is included if - this value is set to True. - :param bva_distance_scale_factor: Scaling factor for the bond valence analyzer (this might be different whether - the structure is an experimental one, an LDA or a GGA relaxed one, or any other relaxation scheme (where - under- or over-estimation of bond lengths is known). - :param structure_refinement: Refinement of the structure. Can be "none", "refined" or "symmetrized". - :param spg_analyzer_options: Options for the SpaceGroupAnalyzer (dictionary specifying "symprec" - and "angle_tolerance". See pymatgen's SpaceGroupAnalyzer for more information. + + Args: + centering_type: Type of the reference point (centering) 'standard', 'centroid' or 'central_site' + include_central_site_in_centroid: In case centering_type is 'centroid', the central site is included if + this value is set to True. + bva_distance_scale_factor: Scaling factor for the bond valence analyzer (this might be different whether + the structure is an experimental one, an LDA or a GGA relaxed one, or any other relaxation scheme (where + under- or over-estimation of bond lengths is known). + structure_refinement: Refinement of the structure. Can be "none", "refined" or "symmetrized". + spg_analyzer_options: Options for the SpaceGroupAnalyzer (dictionary specifying "symprec" + and "angle_tolerance". See pymatgen's SpaceGroupAnalyzer for more information. """ self.centering_type = centering_type self.include_central_site_in_centroid = include_central_site_in_centroid @@ -429,8 +454,10 @@ def setup_parameter(self, parameter, value): """ Setup of one specific parameter to the given value. The other parameters are unchanged. See setup_parameters method for the list of possible parameters - :param parameter: Parameter to setup/update - :param value: Value of the parameter. + + Args: + parameter: Parameter to setup/update + value: Value of the parameter. """ self.__dict__[parameter] = value @@ -438,7 +465,9 @@ def setup_structure(self, structure: Structure): """ Sets up the structure for which the coordination geometries have to be identified. The structure is analyzed with the space group analyzer and a refined structure is used - :param structure: A pymatgen Structure. + + Args: + structure: A pymatgen Structure. """ self.initial_structure = structure.copy() if self.structure_refinement == self.STRUCTURE_REFINEMENT_NONE: @@ -477,10 +506,12 @@ def set_structure(self, lattice: Lattice, species, coords, coords_are_cartesian) """ Sets up the pymatgen structure for which the coordination geometries have to be identified starting from the lattice, the species and the coordinates - :param lattice: The lattice of the structure - :param species: The species on the sites - :param coords: The coordinates of the sites - :param coords_are_cartesian: If set to True, the coordinates are given in Cartesian coordinates. + + Args: + lattice: The lattice of the structure + species: The species on the sites + coords: The coordinates of the sites + coords_are_cartesian: If set to True, the coordinates are given in Cartesian coordinates. """ self.setup_structure(Structure(lattice, species, coords, coords_are_cartesian)) @@ -494,12 +525,13 @@ def compute_coordination_environments( initial_structure_environments=None, ): """ - :param structure: - :param indices: - :param only_cations: - :param strategy: - :param valences: - :param initial_structure_environments: + Args: + structure: + indices: + only_cations: + strategy: + valences: + initial_structure_environments: """ self.setup_structure(structure=structure) if valences == "bond-valence-analysis": @@ -553,34 +585,36 @@ def compute_structure_environments( """ Computes and returns the StructureEnvironments object containing all the information about the coordination environments in the structure - :param excluded_atoms: Atoms for which the coordination geometries does not have to be identified - :param only_atoms: If not set to None, atoms for which the coordination geometries have to be identified - :param only_cations: If set to True, will only compute environments for cations - :param only_indices: If not set to None, will only compute environments the atoms of the given indices - :param maximum_distance_factor: If not set to None, neighbors beyond - maximum_distance_factor*closest_neighbor_distance are not considered - :param minimum_angle_factor: If not set to None, neighbors for which the angle is lower than - minimum_angle_factor*largest_angle_neighbor are not considered - :param max_cn: maximum coordination number to be considered - :param min_cn: minimum coordination number to be considered - :param only_symbols: if not set to None, consider only coordination environments with the given symbols - :param valences: valences of the atoms - :param additional_conditions: additional conditions to be considered in the bonds (example : only bonds - between cation and anion - :param info: additional info about the calculation - :param timelimit: time limit (in secs) after which the calculation of the StructureEnvironments object stops - :param initial_structure_environments: initial StructureEnvironments object (most probably incomplete) - :param get_from_hints: whether to add neighbors sets from "hints" (e.g. capped environment => test the - neighbors without the cap) - :param voronoi_normalized_distance_tolerance: tolerance for the normalized distance used to distinguish - neighbors sets - :param voronoi_normalized_angle_tolerance: tolerance for the normalized angle used to distinguish - neighbors sets - :param voronoi_distance_cutoff: determines distance of considered neighbors. Especially important to increase it - for molecules in a box. - :param recompute: whether to recompute the sites already computed (when initial_structure_environments - is not None) - :param optimization: optimization algorithm + + Args: + excluded_atoms: Atoms for which the coordination geometries does not have to be identified + only_atoms: If not set to None, atoms for which the coordination geometries have to be identified + only_cations: If set to True, will only compute environments for cations + only_indices: If not set to None, will only compute environments the atoms of the given indices + maximum_distance_factor: If not set to None, neighbors beyond + maximum_distance_factor*closest_neighbor_distance are not considered + minimum_angle_factor: If not set to None, neighbors for which the angle is lower than + minimum_angle_factor*largest_angle_neighbor are not considered + max_cn: maximum coordination number to be considered + min_cn: minimum coordination number to be considered + only_symbols: if not set to None, consider only coordination environments with the given symbols + valences: valences of the atoms + additional_conditions: additional conditions to be considered in the bonds (example : only bonds + between cation and anion + info: additional info about the calculation + timelimit: time limit (in secs) after which the calculation of the StructureEnvironments object stops + initial_structure_environments: initial StructureEnvironments object (most probably incomplete) + get_from_hints: whether to add neighbors sets from "hints" (e.g. capped environment => test the + neighbors without the cap) + voronoi_normalized_distance_tolerance: tolerance for the normalized distance used to distinguish + neighbors sets + voronoi_normalized_angle_tolerance: tolerance for the normalized angle used to distinguish + neighbors sets + voronoi_distance_cutoff: determines distance of considered neighbors. Especially important to increase it + for molecules in a box. + recompute: whether to recompute the sites already computed (when initial_structure_environments + is not None) + optimization: optimization algorithm Returns: The StructureEnvironments object containing all the information about the coordination @@ -848,13 +882,14 @@ def compute_structure_environments( def update_nb_set_environments(self, se, isite, cn, inb_set, nb_set, recompute=False, optimization=None): """ - :param se: - :param isite: - :param cn: - :param inb_set: - :param nb_set: - :param recompute: - :param optimization: + Args: + se: + isite: + cn: + inb_set: + nb_set: + recompute: + optimization: """ ce = se.get_coordination_environments(isite=isite, cn=cn, nb_set=nb_set) if ce is not None and not recompute: @@ -915,8 +950,10 @@ def update_nb_set_environments(self, se, isite, cn, inb_set, nb_set, recompute=F def setup_local_geometry(self, isite, coords, optimization=None): """ Sets up the AbstractGeometry for the local geometry of site with index isite. - :param isite: Index of the site for which the local geometry has to be set up - :param coords: The coordinates of the (local) neighbors. + + Args: + isite: Index of the site for which the local geometry has to be set up + coords: The coordinates of the (local) neighbors. """ self.local_geometry = AbstractGeometry( central_site=self.structure.cart_coords[isite], @@ -939,15 +976,16 @@ def setup_test_perfect_environment( points=None, ): """ - :param symbol: - :param randomness: - :param max_random_dist: - :param symbol_type: - :param indices: - :param random_translation: - :param random_rotation: - :param random_scale: - :param points: + Args: + symbol: + randomness: + max_random_dist: + symbol_type: + indices: + random_translation: + random_rotation: + random_scale: + points: """ if symbol_type == "IUPAC": cg = self.allcg.get_geometry_from_IUPAC_symbol(symbol) @@ -1072,7 +1110,9 @@ def setup_test_perfect_environment( def setup_random_structure(self, coordination): """ Sets up a purely random structure with a given coordination. - :param coordination: coordination number for the random structure. + + Args: + coordination: coordination number for the random structure. """ aa = 0.4 bb = -0.2 @@ -1090,7 +1130,9 @@ def setup_random_structure(self, coordination): def setup_random_indices_local_geometry(self, coordination): """ Sets up random indices for the local geometry, for testing purposes - :param coordination: coordination of the local geometry. + + Args: + coordination: coordination of the local geometry. """ self.icentral_site = 0 self.indices = list(range(1, coordination + 1)) @@ -1099,7 +1141,9 @@ def setup_random_indices_local_geometry(self, coordination): def setup_ordered_indices_local_geometry(self, coordination): """ Sets up ordered indices for the local geometry, for testing purposes - :param coordination: coordination of the local geometry. + + Args: + coordination: coordination of the local geometry. """ self.icentral_site = 0 self.indices = list(range(1, coordination + 1)) @@ -1107,7 +1151,9 @@ def setup_ordered_indices_local_geometry(self, coordination): def setup_explicit_indices_local_geometry(self, explicit_indices): """ Sets up explicit indices for the local geometry, for testing purposes - :param explicit_indices: explicit indices for the neighbors (set of numbers + + Args: + explicit_indices: explicit indices for the neighbors (set of numbers from 0 to CN-1 in a given order). """ self.icentral_site = 0 @@ -1306,7 +1352,8 @@ def coordination_geometry_symmetry_measures( permutations depending on the permutation setup. Depending on the parameters of the LocalGeometryFinder and on the coordination geometry, different methods are called. - :param coordination_geometry: Coordination geometry for which the symmetry measures are looked for + Args: + coordination_geometry: Coordination geometry for which the symmetry measures are looked for Raises: NotImplementedError: if the permutation_setup does not exist @@ -1354,7 +1401,8 @@ def coordination_geometry_symmetry_measures_sepplane_optim( permutations depending on the permutation setup. Depending on the parameters of the LocalGeometryFinder and on the coordination geometry, different methods are called. - :param coordination_geometry: Coordination geometry for which the symmetry measures are looked for + Args: + coordination_geometry: Coordination geometry for which the symmetry measures are looked for Raises: NotImplementedError: if the permutation_setup does not exist @@ -1392,7 +1440,9 @@ def coordination_geometry_symmetry_measures_standard( Returns the symmetry measures for a set of permutations (whose setup depends on the coordination geometry) for the coordination geometry "coordination_geometry". Standard implementation looking for the symmetry measures of each permutation - :param coordination_geometry: The coordination geometry to be investigated + + Args: + coordination_geometry: The coordination geometry to be investigated Returns: The symmetry measures for the given coordination geometry for each permutation investigated. @@ -1471,7 +1521,9 @@ def coordination_geometry_symmetry_measures_separation_plane( """ Returns the symmetry measures of the given coordination geometry "coordination_geometry" using separation facets to reduce the complexity of the system. Caller to the refined 2POINTS, 3POINTS and other ... - :param coordination_geometry: The coordination geometry to be investigated + + Args: + coordination_geometry: The coordination geometry to be investigated Returns: The symmetry measures for the given coordination geometry for each plane and permutation investigated. @@ -1995,8 +2047,10 @@ def coordination_geometry_symmetry_measures_fallback_random( Returns the symmetry measures for a random set of permutations for the coordination geometry "coordination_geometry". Fallback implementation for the plane separation algorithms measures of each permutation - :param coordination_geometry: The coordination geometry to be investigated - :param NRANDOM: Number of random permutations to be tested + + Args: + coordination_geometry: The coordination geometry to be investigated + NRANDOM: Number of random permutations to be tested Returns: The symmetry measures for the given coordination geometry for each permutation investigated. diff --git a/pymatgen/analysis/chemenv/utils/chemenv_config.py b/pymatgen/analysis/chemenv/utils/chemenv_config.py index bb641207c25..e311f469a16 100644 --- a/pymatgen/analysis/chemenv/utils/chemenv_config.py +++ b/pymatgen/analysis/chemenv/utils/chemenv_config.py @@ -42,7 +42,10 @@ class ChemEnvConfig: ) def __init__(self, package_options=None): - """:param package_options:""" + """ + Args: + package_options: + """ if SETTINGS.get("PMG_MAPI_KEY"): self.materials_project_configuration = SETTINGS.get("PMG_MAPI_KEY") else: @@ -135,7 +138,9 @@ def package_options_description(self): def save(self, root_dir=None): """ Save the options. - :param root_dir: + + Args: + root_dir: """ if root_dir is None: home = expanduser("~") @@ -158,7 +163,9 @@ def save(self, root_dir=None): def auto_load(cls, root_dir=None): """ Autoload options. - :param root_dir: + + Args: + root_dir: """ if root_dir is None: home = expanduser("~") diff --git a/pymatgen/analysis/chemenv/utils/chemenv_errors.py b/pymatgen/analysis/chemenv/utils/chemenv_errors.py index 8e1cae20e2b..479d61454d0 100644 --- a/pymatgen/analysis/chemenv/utils/chemenv_errors.py +++ b/pymatgen/analysis/chemenv/utils/chemenv_errors.py @@ -16,9 +16,10 @@ class AbstractChemenvError(Exception): def __init__(self, cls, method, msg): """ - :param cls: - :param method: - :param msg: + Args: + cls: + method: + msg: """ self.cls = cls self.method = method @@ -32,7 +33,10 @@ class NeighborsNotComputedChemenvError(AbstractChemenvError): """Neighbors not computed error.""" def __init__(self, site): - """:param site:""" + """ + Args: + site: + """ self.site = site def __str__(self): @@ -43,7 +47,10 @@ class EquivalentSiteSearchError(AbstractChemenvError): """Equivalent site search error.""" def __init__(self, site): - """:param site:""" + """ + Args: + site: + """ self.site = site def __str__(self): @@ -54,7 +61,10 @@ class SolidAngleError(AbstractChemenvError): """Solid angle error.""" def __init__(self, cosinus): - """:param cosinus:""" + """ + Args: + cosinus: + """ self.cosinus = cosinus def __str__(self): @@ -66,9 +76,10 @@ class ChemenvError(Exception): def __init__(self, cls: str, method: str, msg: str): """ - :param cls: - :param method: - :param msg: + Args: + cls: + method: + msg: """ self.cls = cls self.method = method diff --git a/pymatgen/analysis/chemenv/utils/coordination_geometry_utils.py b/pymatgen/analysis/chemenv/utils/coordination_geometry_utils.py index 447b7bd29a1..27be649f208 100644 --- a/pymatgen/analysis/chemenv/utils/coordination_geometry_utils.py +++ b/pymatgen/analysis/chemenv/utils/coordination_geometry_utils.py @@ -15,6 +15,7 @@ if TYPE_CHECKING: from numpy.typing import ArrayLike + from typing_extensions import Self __author__ = "David Waroquiers" __copyright__ = "Copyright 2012, The Materials Project" @@ -28,7 +29,9 @@ def get_lower_and_upper_f(surface_calculation_options): """Get the lower and upper functions defining a surface in the distance-angle space of neighbors. - :param surface_calculation_options: Options for the surface. + Args: + surface_calculation_options: Options for the surface. + Returns: Dictionary containing the "lower" and "upper" functions for the surface. """ @@ -388,8 +391,10 @@ def solid_angle(center, coords): def vectorsToMatrix(aa, bb): """ Performs the vector multiplication of the elements of two vectors, constructing the 3x3 matrix. - :param aa: One vector of size 3 - :param bb: Another vector of size 3 + + Args: + aa: One vector of size 3 + bb: Another vector of size 3 Returns: A 3x3 matrix M composed of the products of the elements of aa and bb : M_ij = aa_i * bb_j. @@ -404,8 +409,9 @@ def vectorsToMatrix(aa, bb): def matrixTimesVector(MM, aa): """ - :param MM: A matrix of size 3x3 - :param aa: A vector of size 3 + Args: + MM: A matrix of size 3x3 + aa: A vector of size 3 Returns: A vector of size 3 which is the product of the matrix by the vector @@ -419,8 +425,10 @@ def matrixTimesVector(MM, aa): def rotateCoords(coords, R): """ Rotate the list of points using rotation matrix R - :param coords: List of points to be rotated - :param R: Rotation matrix + + Args: + coords: List of points to be rotated + R: Rotation matrix Returns: List of rotated points. @@ -435,8 +443,10 @@ def rotateCoords(coords, R): def rotateCoordsOpt(coords, R): """ Rotate the list of points using rotation matrix R - :param coords: List of points to be rotated - :param R: Rotation matrix + + Args: + coords: List of points to be rotated + R: Rotation matrix Returns: List of rotated points. @@ -448,10 +458,12 @@ def changebasis(uu, vv, nn, pps): """ For a list of points given in standard coordinates (in terms of e1, e2 and e3), returns the same list expressed in the basis (uu, vv, nn), which is supposed to be orthonormal. - :param uu: First vector of the basis - :param vv: Second vector of the basis - :param nn: Third vector of the basis - :param pps: List of points in basis (e1, e2, e3) + + Args: + uu: First vector of the basis + vv: Second vector of the basis + nn: Third vector of the basis + pps: List of points in basis (e1, e2, e3) Returns: List of points in basis (uu, vv, nn). """ @@ -474,10 +486,13 @@ def collinear(p1, p2, p3=None, tolerance=0.25): triangle is less than (tolerance x largest_triangle), then the three points are considered collinear. The largest_triangle is defined as the right triangle whose legs are the two smallest distances between the three points ie, its area is : 0.5 x (min(|p2-p1|,|p3-p1|,|p3-p2|) x second_min(|p2-p1|,|p3-p1|,|p3-p2|)) - :param p1: First point - :param p2: Second point - :param p3: Third point (origin [0.0, 0.0, 0.0 if not given]) - :param tolerance: Area tolerance for the collinearity test (0.25 gives about 0.125 deviation from the line) + + Args: + p1: First point + p2: Second point + p3: Third point (origin [0.0, 0.0, 0.0 if not given]) + tolerance: Area tolerance for the collinearity test (0.25 gives about 0.125 deviation from the line) + Returns: bool: True if the three points are considered as collinear within the given tolerance. """ @@ -494,7 +509,9 @@ def collinear(p1, p2, p3=None, tolerance=0.25): def anticlockwise_sort(pps): """ Sort a list of 2D points in anticlockwise order - :param pps: List of points to be sorted + + Args: + pps: List of points to be sorted Returns: Sorted list of points. @@ -512,7 +529,9 @@ def anticlockwise_sort(pps): def anticlockwise_sort_indices(pps): """ Returns the indices that would sort a list of 2D points in anticlockwise order - :param pps: List of points to be sorted + + Args: + pps: List of points to be sorted Returns: Indices of the sorted list of points. @@ -526,7 +545,9 @@ def anticlockwise_sort_indices(pps): def sort_separation(separation): """Sort a separation. - :param separation: Initial separation. + Args: + separation: Initial separation. + Returns: Sorted list of separation. """ @@ -538,7 +559,8 @@ def sort_separation(separation): def sort_separation_tuple(separation): """Sort a separation. - :param separation: Initial separation + Args: + separation: Initial separation Returns: Sorted tuple of separation @@ -559,8 +581,10 @@ def sort_separation_tuple(separation): def separation_in_list(separation_indices, separation_indices_list): """ Checks if the separation indices of a plane are already in the list - :param separation_indices: list of separation indices (three arrays of integers) - :param separation_indices_list: list of the list of separation indices to be compared to + + Args: + separation_indices: list of separation indices (three arrays of integers) + separation_indices_list: list of the list of separation indices to be compared to Returns: bool: True if the separation indices are already in the list. @@ -575,9 +599,11 @@ def separation_in_list(separation_indices, separation_indices_list): def is_anion_cation_bond(valences, ii, jj) -> bool: """ Checks if two given sites are an anion and a cation. - :param valences: list of site valences - :param ii: index of a site - :param jj: index of another site + + Args: + valences: list of site valences + ii: index of a site + jj: index of another site Returns: bool: True if one site is an anion and the other is a cation (based on valences). @@ -619,7 +645,9 @@ class Plane: def __init__(self, coefficients, p1=None, p2=None, p3=None): """ Initializes a plane from the 4 coefficients a, b, c and d of ax + by + cz + d = 0 - :param coefficients: abcd coefficients of the plane. + + Args: + coefficients: abcd coefficients of the plane. """ # Initializes the normal vector self.normal_vector = np.array([coefficients[0], coefficients[1], coefficients[2]], float) @@ -652,8 +680,9 @@ def __init__(self, coefficients, p1=None, p2=None, p3=None): def init_3points(self, non_zeros, zeros): """Initialize three random points on this plane. - :param non_zeros: Indices of plane coefficients ([a, b, c]) that are not zero. - :param zeros: Indices of plane coefficients ([a, b, c]) that are equal to zero. + Args: + non_zeros: Indices of plane coefficients ([a, b, c]) that are not zero. + zeros: Indices of plane coefficients ([a, b, c]) that are equal to zero. """ if len(non_zeros) == 3: self.p1 = np.array([-self.d / self.a, 0.0, 0.0], float) @@ -690,8 +719,10 @@ def __str__(self): def is_in_plane(self, pp, dist_tolerance) -> bool: """ Determines if point pp is in the plane within the tolerance dist_tolerance - :param pp: point to be tested - :param dist_tolerance: tolerance on the distance to the plane within which point pp is considered in the plane + + Args: + pp: point to be tested + dist_tolerance: tolerance on the distance to the plane within which point pp is considered in the plane Returns: bool: True if pp is in the plane. @@ -701,7 +732,9 @@ def is_in_plane(self, pp, dist_tolerance) -> bool: def is_same_plane_as(self, plane) -> bool: """ Checks whether the plane is identical to another Plane "plane" - :param plane: Plane to be compared to + + Args: + plane: Plane to be compared to Returns: bool: True if the two facets are identical. @@ -711,7 +744,9 @@ def is_same_plane_as(self, plane) -> bool: def is_in_list(self, plane_list) -> bool: """ Checks whether the plane is identical to one of the Planes in the plane_list list of Planes - :param plane_list: List of Planes to be compared to + + Args: + plane_list: List of Planes to be compared to Returns: bool: True if the plane is in the list. @@ -723,9 +758,11 @@ def indices_separate(self, points, dist_tolerance): Returns three lists containing the indices of the points lying on one side of the plane, on the plane and on the other side of the plane. The dist_tolerance parameter controls the tolerance to which a point is considered to lie on the plane or not (distance to the plane) - :param points: list of points - :param dist_tolerance: tolerance to which a point is considered to lie on the plane - or not (distance to the plane) + + Args: + points: list of points + dist_tolerance: tolerance to which a point is considered to lie on the plane + or not (distance to the plane) Returns: The lists of indices of the points on one side of the plane, on the plane and @@ -746,7 +783,9 @@ def indices_separate(self, points, dist_tolerance): def distance_to_point(self, point): """ Computes the absolute distance from the plane to the point - :param point: Point for which distance is computed + + Args: + point: Point for which distance is computed Returns: Distance between the plane and the point. @@ -757,7 +796,9 @@ def distances(self, points): """ Computes the distances from the plane to each of the points. Positive distances are on the side of the normal of the plane while negative distances are on the other side - :param points: Points for which distances are computed + + Args: + points: Points for which distances are computed Returns: Distances from the plane to the points (positive values on the side of the normal to the plane, @@ -770,8 +811,10 @@ def distances_indices_sorted(self, points, sign=False): Computes the distances from the plane to each of the points. Positive distances are on the side of the normal of the plane while negative distances are on the other side. Indices sorting the points from closest to furthest is also computed. - :param points: Points for which distances are computed - :param sign: Whether to add sign information in the indices sorting the points distances + + Args: + points: Points for which distances are computed + sign: Whether to add sign information in the indices sorting the points distances Returns: Distances from the plane to the points (positive values on the side of the normal to the plane, @@ -791,11 +834,13 @@ def distances_indices_groups(self, points, delta=None, delta_factor=0.05, sign=F to furthest is also computed. Grouped indices are also given, for which indices of the distances that are separated by less than delta are grouped together. The delta parameter is either set explicitly or taken as a fraction (using the delta_factor parameter) of the maximal point distance. - :param points: Points for which distances are computed - :param delta: Distance interval for which two points are considered in the same group. - :param delta_factor: If delta is None, the distance interval is taken as delta_factor times the maximal + + Args: + points: Points for which distances are computed + delta: Distance interval for which two points are considered in the same group. + delta_factor: If delta is None, the distance interval is taken as delta_factor times the maximal point distance. - :param sign: Whether to add sign information in the indices sorting the points distances + sign: Whether to add sign information in the indices sorting the points distances Returns: Distances from the plane to the points (positive values on the side of the normal to the plane, @@ -819,7 +864,9 @@ def distances_indices_groups(self, points, delta=None, delta_factor=0.05, sign=F def projectionpoints(self, pps): """ Projects each points in the point list pps on plane and returns the list of projected points - :param pps: List of points to project on plane + + Args: + pps: List of points to project on plane Returns: List of projected point on plane. @@ -845,7 +892,9 @@ def project_and_to2dim_ordered_indices(self, pps, plane_center="mean"): """ Projects each points in the point list pps on plane and returns the indices that would sort the list of projected points in anticlockwise order - :param pps: List of points to project on plane + + Args: + pps: List of points to project on plane Returns: List of indices that would sort the list of projected points. @@ -856,7 +905,9 @@ def project_and_to2dim_ordered_indices(self, pps, plane_center="mean"): def project_and_to2dim(self, pps, plane_center): """ Projects the list of points pps to the plane and changes the basis from 3D to the 2D basis of the plane - :param pps: List of points to be projected + + Args: + pps: List of points to be projected Returns: :raise: @@ -883,8 +934,9 @@ def project_and_to2dim(self, pps, plane_center): def fit_error(self, points, fit="least_square_distance"): """Evaluate the error for a list of points with respect to this plane. - :param points: List of points. - :param fit: Type of fit error. + Args: + points: List of points. + fit: Type of fit error. Returns: Error for a list of points with respect to this plane. @@ -898,7 +950,8 @@ def fit_error(self, points, fit="least_square_distance"): def fit_least_square_distance_error(self, points): """Evaluate the sum of squared distances error for a list of points with respect to this plane. - :param points: List of points. + Args: + points: List of points. Returns: Sum of squared distances error for a list of points with respect to this plane. @@ -908,7 +961,8 @@ def fit_least_square_distance_error(self, points): def fit_maximum_distance_error(self, points): """Evaluate the max distance error for a list of points with respect to this plane. - :param points: List of points. + Args: + points: List of points. Returns: Max distance error for a list of points with respect to this plane. @@ -969,11 +1023,12 @@ def crosses_origin(self): return self._crosses_origin @classmethod - def from_2points_and_origin(cls, p1, p2): + def from_2points_and_origin(cls, p1, p2) -> Self: """Initializes plane from two points and the origin. - :param p1: First point. - :param p2: Second point. + Args: + p1: First point. + p2: Second point. Returns: Plane. @@ -981,12 +1036,13 @@ def from_2points_and_origin(cls, p1, p2): return cls.from_3points(p1, p2, np.zeros(3)) @classmethod - def from_3points(cls, p1, p2, p3): + def from_3points(cls, p1, p2, p3) -> Self: """Initializes plane from three points. - :param p1: First point. - :param p2: Second point. - :param p3: Third point. + Args: + p1: First point. + p2: Second point. + p3: Third point. Returns: Plane. @@ -1001,13 +1057,14 @@ def from_3points(cls, p1, p2, p3): return cls(coefficients, p1=p1, p2=p2, p3=p3) @classmethod - def from_npoints(cls, points, best_fit="least_square_distance"): + def from_npoints(cls, points, best_fit="least_square_distance") -> Self: """Initializes plane from a list of points. If the number of points is larger than 3, will use a least square fitting or max distance fitting. - :param points: List of points. - :param best_fit: Type of fitting procedure for more than 3 points. + Args: + points: List of points. + best_fit: Type of fitting procedure for more than 3 points. Returns: Plane @@ -1020,13 +1077,15 @@ def from_npoints(cls, points, best_fit="least_square_distance"): return cls.from_npoints_least_square_distance(points) if best_fit == "maximum_distance": return cls.from_npoints_maximum_distance(points) - return None + + raise ValueError("Cannot initialize Plane.") @classmethod - def from_npoints_least_square_distance(cls, points): + def from_npoints_least_square_distance(cls, points) -> Self: """Initializes plane from a list of points using a least square fitting procedure. - :param points: List of points. + Args: + points: List of points. Returns: Plane. @@ -1048,14 +1107,15 @@ def from_npoints_least_square_distance(cls, points): return cls(coefficients) @classmethod - def perpendicular_bisector(cls, p1, p2): + def perpendicular_bisector(cls, p1, p2) -> Self: """Initialize a plane from the perpendicular bisector of two points. The perpendicular bisector of two points is the plane perpendicular to the vector joining these two points and passing through the middle of the segment joining the two points. - :param p1: First point. - :param p2: Second point. + Args: + p1: First point. + p2: Second point. Returns: Plane. @@ -1066,10 +1126,11 @@ def perpendicular_bisector(cls, p1, p2): return cls(np.array([normal_vector[0], normal_vector[1], normal_vector[2], dd], float)) @classmethod - def from_npoints_maximum_distance(cls, points): + def from_npoints_maximum_distance(cls, points) -> Self: """Initializes plane from a list of points using a max distance fitting procedure. - :param points: List of points. + Args: + points: List of points. Returns: Plane. @@ -1095,13 +1156,14 @@ def from_npoints_maximum_distance(cls, points): return cls(np.array([normal_vector[0], normal_vector[1], normal_vector[2], dd], float)) @classmethod - def from_coefficients(cls, a, b, c, d): + def from_coefficients(cls, a, b, c, d) -> Self: """Initialize plane from its coefficients. - :param a: a coefficient of the plane. - :param b: b coefficient of the plane. - :param c: c coefficient of the plane. - :param d: d coefficient of the plane. + Args: + a: a coefficient of the plane. + b: b coefficient of the plane. + c: c coefficient of the plane. + d: d coefficient of the plane. Returns: Plane. diff --git a/pymatgen/analysis/chemenv/utils/defs_utils.py b/pymatgen/analysis/chemenv/utils/defs_utils.py index 390ad9170b8..01bbc9e8450 100644 --- a/pymatgen/analysis/chemenv/utils/defs_utils.py +++ b/pymatgen/analysis/chemenv/utils/defs_utils.py @@ -70,9 +70,10 @@ class AdditionalConditions: def check_condition(self, condition, structure: Structure, parameters): """ - :param condition: - :param structure: - :param parameters: + Args: + condition: + structure: + parameters: """ if condition == self.NONE: return True diff --git a/pymatgen/analysis/chemenv/utils/func_utils.py b/pymatgen/analysis/chemenv/utils/func_utils.py index 92db68fa7e7..7ad187de561 100644 --- a/pymatgen/analysis/chemenv/utils/func_utils.py +++ b/pymatgen/analysis/chemenv/utils/func_utils.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import ClassVar +from typing import TYPE_CHECKING, ClassVar import numpy as np @@ -14,6 +14,9 @@ smoothstep, ) +if TYPE_CHECKING: + from typing_extensions import Self + __author__ = "David Waroquiers" __copyright__ = "Copyright 2012, The Materials Project" __credits__ = "Geoffroy Hautier" @@ -31,8 +34,9 @@ class AbstractRatioFunction: def __init__(self, function, options_dict=None): """Constructor for AbstractRatioFunction. - :param function: Ration function name. - :param options_dict: Dictionary containing the parameters for the ratio function. + Args: + function: Ration function name. + options_dict: Dictionary containing the parameters for the ratio function. """ if function not in self.ALLOWED_FUNCTIONS: raise ValueError(f"{function=!r} is not allowed in RatioFunction of type {type(self).__name__}") @@ -43,7 +47,8 @@ def __init__(self, function, options_dict=None): def setup_parameters(self, options_dict): """Set up the parameters for this ratio function. - :param options_dict: Dictionary containing the parameters for the ratio function. + Args: + options_dict: Dictionary containing the parameters for the ratio function. """ function_options = self.ALLOWED_FUNCTIONS[self.function] if len(function_options) > 0: @@ -88,7 +93,8 @@ def setup_parameters(self, options_dict): def evaluate(self, value): """Evaluate the ratio function for the given value. - :param value: Value for which ratio function has to be evaluated. + Args: + value: Value for which ratio function has to be evaluated. Returns: Ratio function corresponding to the value. @@ -96,13 +102,11 @@ def evaluate(self, value): return self.eval(value) @classmethod - def from_dict(cls, dct): + def from_dict(cls, dct: dict) -> Self: """Construct ratio function from dict. - :param dct: Dict representation of the ratio function - - Returns: - Ratio function object. + Args: + dct: Dict representation of the ratio function """ return cls(function=dct["function"], options_dict=dct["options"]) @@ -126,7 +130,8 @@ def power2_decreasing_exp(self, vals): The values (i.e. "x"), are scaled to the "max" parameter. The "a" constant correspond to the "alpha" parameter. - :param vals: Values for which the ratio function has to be evaluated. + Args: + vals: Values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the values. @@ -138,7 +143,8 @@ def smootherstep(self, vals): The values (i.e. "x"), are scaled between the "lower" and "upper" parameters. - :param vals: Values for which the ratio function has to be evaluated. + Args: + vals: Values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the values. @@ -150,7 +156,8 @@ def smoothstep(self, vals): The values (i.e. "x"), are scaled between the "lower" and "upper" parameters. - :param vals: Values for which the ratio function has to be evaluated. + Args: + vals: Values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the values. @@ -162,7 +169,8 @@ def inverse_smootherstep(self, vals): The values (i.e. "x"), are scaled between the "lower" and "upper" parameters. - :param vals: Values for which the ratio function has to be evaluated. + Args: + vals: Values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the values. @@ -174,7 +182,8 @@ def inverse_smoothstep(self, vals): The values (i.e. "x"), are scaled between the "lower" and "upper" parameters. - :param vals: Values for which the ratio function has to be evaluated. + Args: + vals: Values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the values. @@ -186,7 +195,8 @@ def power2_inverse_decreasing(self, vals): The values (i.e. "x"), are scaled to the "max" parameter. - :param vals: Values for which the ratio function has to be evaluated. + Args: + vals: Values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the values. @@ -198,7 +208,8 @@ def power2_inverse_power2_decreasing(self, vals): The values (i.e. "x"), are scaled to the "max" parameter. - :param vals: Values for which the ratio function has to be evaluated. + Args: + vals: Values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the values. @@ -228,7 +239,8 @@ def power2_decreasing_exp(self, vals): The CSM values (i.e. "x"), are scaled to the "max_csm" parameter. The "a" constant correspond to the "alpha" parameter. - :param vals: CSM values for which the ratio function has to be evaluated. + Args: + vals: CSM values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the CSM values. @@ -240,7 +252,8 @@ def smootherstep(self, vals): The CSM values (i.e. "x"), are scaled between the "lower_csm" and "upper_csm" parameters. - :param vals: CSM values for which the ratio function has to be evaluated. + Args: + vals: CSM values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the CSM values. @@ -256,7 +269,8 @@ def smoothstep(self, vals): The CSM values (i.e. "x"), are scaled between the "lower_csm" and "upper_csm" parameters. - :param vals: CSM values for which the ratio function has to be evaluated. + Args: + vals: CSM values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the CSM values. @@ -270,7 +284,8 @@ def smoothstep(self, vals): def fractions(self, data): """Get the fractions from the CSM ratio function applied to the data. - :param data: List of CSM values to estimate fractions. + Args: + data: List of CSM values to estimate fractions. Returns: Corresponding fractions for each CSM. @@ -285,7 +300,8 @@ def fractions(self, data): def mean_estimator(self, data): """Get the weighted CSM using this CSM ratio function applied to the data. - :param data: List of CSM values to estimate the weighted CSM. + Args: + data: List of CSM values to estimate the weighted CSM. Returns: Weighted CSM from this ratio function. @@ -323,7 +339,8 @@ def power2_inverse_decreasing(self, vals): The CSM values (i.e. "x"), are scaled to the "max_csm" parameter. The "a" constant correspond to the "alpha" parameter. - :param vals: CSM values for which the ratio function has to be evaluated. + Args: + vals: CSM values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the CSM values. @@ -336,7 +353,8 @@ def power2_inverse_power2_decreasing(self, vals): The CSM values (i.e. "x"), are scaled to the "max_csm" parameter. The "a" constant correspond to the "alpha" parameter. - :param vals: CSM values for which the ratio function has to be evaluated. + Args: + vals: CSM values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the CSM values. @@ -346,7 +364,8 @@ def power2_inverse_power2_decreasing(self, vals): def fractions(self, data): """Get the fractions from the CSM ratio function applied to the data. - :param data: List of CSM values to estimate fractions. + Args: + data: List of CSM values to estimate fractions. Returns: Corresponding fractions for each CSM. @@ -370,7 +389,8 @@ def fractions(self, data): def mean_estimator(self, data): """Get the weighted CSM using this CSM ratio function applied to the data. - :param data: List of CSM values to estimate the weighted CSM. + Args: + data: List of CSM values to estimate the weighted CSM. Returns: Weighted CSM from this ratio function. @@ -406,7 +426,8 @@ def smootherstep(self, vals): The DeltaCSM values (i.e. "x"), are scaled between the "delta_csm_min" and "delta_csm_max" parameters. - :param vals: DeltaCSM values for which the ratio function has to be evaluated. + Args: + vals: DeltaCSM values for which the ratio function has to be evaluated. Returns: Result of the ratio function applied to the DeltaCSM values. diff --git a/pymatgen/analysis/chemenv/utils/graph_utils.py b/pymatgen/analysis/chemenv/utils/graph_utils.py index bb271b25015..024ccd78efc 100644 --- a/pymatgen/analysis/chemenv/utils/graph_utils.py +++ b/pymatgen/analysis/chemenv/utils/graph_utils.py @@ -4,20 +4,26 @@ import itertools import operator +from typing import TYPE_CHECKING import networkx as nx import numpy as np from monty.json import MSONable +if TYPE_CHECKING: + from typing_extensions import Self + __author__ = "waroquiers" def get_delta(node1, node2, edge_data): """ Get the delta. - :param node1: - :param node2: - :param edge_data: + + Args: + node1: + node2: + edge_data: """ if node1.isite == edge_data["start"] and node2.isite == edge_data["end"]: return np.array(edge_data["delta"], dtype=int) @@ -29,11 +35,13 @@ def get_delta(node1, node2, edge_data): def get_all_simple_paths_edges(graph, source, target, cutoff=None, data=True): """ Get all the simple path and edges. - :param graph: - :param source: - :param target: - :param cutoff: - :param data: + + Args: + graph: + source: + target: + cutoff: + data: """ edge_paths = [] if not graph.is_multigraph(): @@ -136,9 +144,10 @@ class SimpleGraphCycle(MSONable): def __init__(self, nodes, validate=True, ordered=None): """ - :param nodes: - :param validate: - :param ordered: + Args: + nodes: + validate: + ordered: """ self.nodes = tuple(nodes) if validate: @@ -181,7 +190,8 @@ def _is_valid(self, check_strict_ordering=False): def validate(self, check_strict_ordering=False): """ - :param check_strict_ordering: + Args: + check_strict_ordering: """ is_valid, msg = self._is_valid(check_strict_ordering=check_strict_ordering) if not is_valid: @@ -249,7 +259,7 @@ def __eq__(self, other: object) -> bool: return self.nodes == other.nodes @classmethod - def from_edges(cls, edges, edges_are_ordered=True): + def from_edges(cls, edges, edges_are_ordered: bool = True) -> Self: """Constructs SimpleGraphCycle from a list edges. By default, the edges list is supposed to be ordered as it will be @@ -282,7 +292,7 @@ def from_edges(cls, edges, edges_are_ordered=True): nodes.pop() return cls(nodes) - def as_dict(self): + def as_dict(self) -> dict: """MSONable dict""" dct = MSONable.as_dict(self) # Transforming tuple object to a list to allow BSON and MongoDB @@ -290,11 +300,13 @@ def as_dict(self): return dct @classmethod - def from_dict(cls, d, validate=False): + def from_dict(cls, d: dict, validate: bool = False) -> Self: """ Serialize from dict. - :param d: - :param validate: + + Args: + d: + validate: """ return cls(nodes=d["nodes"], validate=validate, ordered=d["ordered"]) @@ -314,10 +326,11 @@ class MultiGraphCycle(MSONable): def __init__(self, nodes, edge_indices, validate=True, ordered=None): """ - :param nodes: - :param edge_indices: - :param validate: - :param ordered: + Args: + nodes: + edge_indices: + validate: + ordered: """ self.nodes = tuple(nodes) self.edge_indices = tuple(edge_indices) @@ -366,13 +379,14 @@ def _is_valid(self, check_strict_ordering=False): def validate(self, check_strict_ordering=False): """ - :param check_strict_ordering: + Args: + check_strict_ordering: """ is_valid, msg = self._is_valid(check_strict_ordering=check_strict_ordering) if not is_valid: raise ValueError(f"MultiGraphCycle is not valid : {msg}") - def order(self, raise_on_fail=True): + def order(self, raise_on_fail: bool = True): """Orders the SimpleGraphCycle. The ordering is performed such that the first node is the "lowest" one @@ -380,7 +394,8 @@ def order(self, raise_on_fail=True): first node. If raise_on_fail is set to True a RuntimeError will be raised if the ordering fails. - :param raise_on_fail: If set to True, will raise a RuntimeError if the ordering fails. + Args: + raise_on_fail: If set to True, will raise a RuntimeError if the ordering fails. """ # always validate the cycle if it needs to be ordered # also validates that the nodes can be strictly ordered @@ -453,7 +468,9 @@ def __eq__(self, other: object) -> bool: def get_all_elementary_cycles(graph): """ - :param graph: + + Args: + graph: """ if not isinstance(graph, nx.Graph): raise TypeError("graph should be a networkx Graph object.") diff --git a/pymatgen/analysis/chemenv/utils/math_utils.py b/pymatgen/analysis/chemenv/utils/math_utils.py index ebe6112f281..1f0175a244a 100644 --- a/pymatgen/analysis/chemenv/utils/math_utils.py +++ b/pymatgen/analysis/chemenv/utils/math_utils.py @@ -44,8 +44,12 @@ def _cartesian_product(lists): def prime_factors(n: int) -> list[int]: """Lists prime factors of a given natural integer, from greatest to smallest - :param n: Natural integer - :rtype : list of all prime factors of the given natural n. + + Args: + n: Natural integer + + Returns: + list of all prime factors of the given natural n. """ idx = 2 while idx <= sqrt(n): @@ -57,13 +61,15 @@ def prime_factors(n: int) -> list[int]: return [n] # n is prime -def _factor_generator(n): +def _factor_generator(n: int) -> dict[int, int]: """ From a given natural integer, returns the prime factors and their multiplicity - :param n: Natural integer + + Args: + n: Natural integer """ p = prime_factors(n) - factors = {} + factors: dict[int, int] = {} for p1 in p: try: factors[p1] += 1 @@ -75,7 +81,9 @@ def _factor_generator(n): def divisors(n): """ From a given natural integer, returns the list of divisors in ascending order - :param n: Natural integer + + Args: + n: Natural integer Returns: List of divisors of n in ascending order. @@ -92,9 +100,11 @@ def divisors(n): def get_center_of_arc(p1, p2, radius): """ - :param p1: - :param p2: - :param radius: + + Args: + p1: + p2: + radius: """ dx = p2[0] - p1[0] dy = p2[1] - p1[1] @@ -110,7 +120,9 @@ def get_center_of_arc(p1, p2, radius): def get_linearly_independent_vectors(vectors_list): """ - :param vectors_list: + + Args: + vectors_list: """ independent_vectors_list = [] for vector in vectors_list: @@ -132,11 +144,13 @@ def get_linearly_independent_vectors(vectors_list): def scale_and_clamp(xx, edge0, edge1, clamp0, clamp1): """ - :param xx: - :param edge0: - :param edge1: - :param clamp0: - :param clamp1: + + Args: + xx: + edge0: + edge1: + clamp0: + clamp1: """ return np.clip((xx - edge0) / (edge1 - edge0), clamp0, clamp1) @@ -144,9 +158,11 @@ def scale_and_clamp(xx, edge0, edge1, clamp0, clamp1): # Step function based on the cumulative distribution function of the normal law def normal_cdf_step(xx, mean, scale): """ - :param xx: - :param mean: - :param scale: + + Args: + xx: + mean: + scale: """ return 0.5 * (1.0 + erf((xx - mean) / (np.sqrt(2.0) * scale))) @@ -160,9 +176,11 @@ def normal_cdf_step(xx, mean, scale): def smoothstep(xx, edges=None, inverse=False): """ - :param xx: - :param edges: - :param inverse: + + Args: + xx: + edges: + inverse: """ if edges is None: xx_clipped = np.clip(xx, 0.0, 1.0) @@ -175,9 +193,11 @@ def smoothstep(xx, edges=None, inverse=False): def smootherstep(xx, edges=None, inverse=False): """ - :param xx: - :param edges: - :param inverse: + + Args: + xx: + edges: + inverse: """ if edges is None: xx_clipped = np.clip(xx, 0.0, 1.0) @@ -190,9 +210,11 @@ def smootherstep(xx, edges=None, inverse=False): def cosinus_step(xx, edges=None, inverse=False): """ - :param xx: - :param edges: - :param inverse: + + Args: + xx: + edges: + inverse: """ if edges is None: xx_clipped = np.clip(xx, 0.0, 1.0) @@ -205,19 +227,23 @@ def cosinus_step(xx, edges=None, inverse=False): def power3_step(xx, edges=None, inverse=False): """ - :param xx: - :param edges: - :param inverse: + + Args: + xx: + edges: + inverse: """ return smoothstep(xx, edges=edges, inverse=inverse) def powern_parts_step(xx, edges=None, inverse=False, nn=2): """ - :param xx: - :param edges: - :param inverse: - :param nn: + + Args: + xx: + edges: + inverse: + nn: """ if edges is None: aa = np.power(0.5, 1.0 - nn) @@ -256,9 +282,11 @@ def powern_parts_step(xx, edges=None, inverse=False, nn=2): def powern_decreasing(xx, edges=None, nn=2): """ - :param xx: - :param edges: - :param nn: + + Args: + xx: + edges: + nn: """ if edges is None: aa = 1.0 / np.power(-1.0, nn) @@ -269,9 +297,11 @@ def powern_decreasing(xx, edges=None, nn=2): def power2_decreasing_exp(xx, edges=None, alpha=1.0): """ - :param xx: - :param edges: - :param alpha: + + Args: + xx: + edges: + alpha: """ if edges is None: aa = 1.0 / np.power(-1.0, 2) @@ -287,9 +317,11 @@ def power2_decreasing_exp(xx, edges=None, alpha=1.0): def power2_tangent_decreasing(xx, edges=None, prefactor=None): """ - :param xx: - :param edges: - :param prefactor: + + Args: + xx: + edges: + prefactor: """ if edges is None: aa = 1.0 / np.power(-1.0, 2) if prefactor is None else prefactor @@ -301,9 +333,11 @@ def power2_tangent_decreasing(xx, edges=None, prefactor=None): def power2_inverse_decreasing(xx, edges=None, prefactor=None): """ - :param xx: - :param edges: - :param prefactor: + + Args: + xx: + edges: + prefactor: """ if edges is None: aa = 1.0 / np.power(-1.0, 2) if prefactor is None else prefactor @@ -315,9 +349,11 @@ def power2_inverse_decreasing(xx, edges=None, prefactor=None): def power2_inverse_power2_decreasing(xx, edges=None, prefactor=None): """ - :param xx: - :param edges: - :param prefactor: + + Args: + xx: + edges: + prefactor: """ if edges is None: aa = 1.0 / np.power(-1.0, 2) if prefactor is None else prefactor @@ -332,10 +368,12 @@ def power2_inverse_power2_decreasing(xx, edges=None, prefactor=None): def power2_inverse_powern_decreasing(xx, edges=None, prefactor=None, powern=2.0): """ - :param xx: - :param edges: - :param prefactor: - :param powern: + + Args: + xx: + edges: + prefactor: + powern: """ if edges is None: aa = 1.0 / np.power(-1.0, 2) if prefactor is None else prefactor diff --git a/pymatgen/analysis/chemenv/utils/scripts_utils.py b/pymatgen/analysis/chemenv/utils/scripts_utils.py index 66394140ce9..5b17eb0df15 100644 --- a/pymatgen/analysis/chemenv/utils/scripts_utils.py +++ b/pymatgen/analysis/chemenv/utils/scripts_utils.py @@ -64,18 +64,19 @@ def draw_cg( """ Draw cg. - :param vis: - :param site: - :param neighbors: - :param cg: - :param perm: - :param perfect2local_map: - :param show_perfect: - :param csm_info: - :param symmetry_measure_type: - :param perfect_radius: - :param show_distorted: - :param faces_color_override: + Args: + site: + vis: + neighbors: + cg: + perm: + perfect2local_map: + show_perfect: + csm_info: + symmetry_measure_type: + perfect_radius: + show_distorted: + faces_color_override: """ if show_perfect: if csm_info is None: @@ -154,12 +155,13 @@ def draw_cg( def visualize(cg, zoom=None, vis=None, factor=1.0, view_index=True, faces_color_override=None): """ Visualizing a coordination geometry - :param cg: - :param zoom: - :param vis: - :param factor: - :param view_index: - :param faces_color_override: + Args: + cg: + zoom: + vis: + factor: + view_index: + faces_color_override: """ if vis is None and StructureVis is not None: vis = StructureVis(show_polyhedron=False, show_unit_cell=False) @@ -192,7 +194,8 @@ def compute_environments(chemenv_configuration): """ Compute the environments. - :param chemenv_configuration: + Args: + chemenv_configuration: """ string_sources = { "cif": {"string": "a Cif file", "regexp": r".*\.cif$"}, diff --git a/pymatgen/analysis/diffraction/xrd.py b/pymatgen/analysis/diffraction/xrd.py index be5e288ebf3..ee41919ff5d 100644 --- a/pymatgen/analysis/diffraction/xrd.py +++ b/pymatgen/analysis/diffraction/xrd.py @@ -215,7 +215,7 @@ def get_pattern(self, structure: Structure, scaled=True, two_theta_range=(0, 90) g_dot_r = np.dot(frac_coords, np.transpose([hkl])).T[0] # Highly vectorized computation of atomic scattering factors. - # Equivalent non-vectorized code is:: + # Equivalent non-vectorized code is: # # for site in structure: # el = site.specie diff --git a/pymatgen/analysis/dimensionality.py b/pymatgen/analysis/dimensionality.py index e4c8d7b931c..123297cdd93 100644 --- a/pymatgen/analysis/dimensionality.py +++ b/pymatgen/analysis/dimensionality.py @@ -51,7 +51,7 @@ def get_dimensionality_larsen(bonded_structure): due to periodic boundary conditions. Requires a StructureGraph object as input. This can be generated using one - of the NearNeighbor classes. For example, using the CrystalNN class:: + of the NearNeighbor classes. For example, using the CrystalNN class: bonded_structure = CrystalNN().get_bonded_structure(structure) @@ -85,7 +85,7 @@ def get_structure_components( structure type or improper connections due to periodic boundary conditions. Requires a StructureGraph object as input. This can be generated using one - of the NearNeighbor classes. For example, using the CrystalNN class:: + of the NearNeighbor classes. For example, using the CrystalNN class: bonded_structure = CrystalNN().get_bonded_structure(structure) diff --git a/pymatgen/analysis/energy_models.py b/pymatgen/analysis/energy_models.py index eb4ff7591ed..57317a340a6 100644 --- a/pymatgen/analysis/energy_models.py +++ b/pymatgen/analysis/energy_models.py @@ -26,7 +26,8 @@ class EnergyModel(MSONable, abc.ABC): @abc.abstractmethod def get_energy(self, structure) -> float: """ - :param structure: Structure + Args: + structure: Structure Returns: Energy value @@ -73,7 +74,8 @@ def __init__(self, real_space_cut=None, recip_space_cut=None, eta=None, acc_fact def get_energy(self, structure: Structure): """ - :param structure: Structure + Args: + structure: Structure Returns: Energy value @@ -121,7 +123,8 @@ def __init__(self, symprec: float = 0.1, angle_tolerance=5): def get_energy(self, structure: Structure): """ - :param structure: Structure + Args: + structure: Structure Returns: Energy value @@ -156,7 +159,8 @@ def __init__(self, j, max_radius): def get_energy(self, structure: Structure): """ - :param structure: Structure + Args: + structure: Structure Returns: Energy value @@ -188,7 +192,8 @@ class NsitesModel(EnergyModel): def get_energy(self, structure: Structure): """ - :param structure: Structure + Args: + structure: Structure Returns: Energy value diff --git a/pymatgen/analysis/eos.py b/pymatgen/analysis/eos.py index 5d751618dce..915f3d93d06 100644 --- a/pymatgen/analysis/eos.py +++ b/pymatgen/analysis/eos.py @@ -522,7 +522,7 @@ class EOS: Fit equation of state for bulk systems. - The following equations are supported:: + The following equations are supported: murnaghan: PRB 28, 5480 (1983) @@ -539,7 +539,7 @@ class EOS: numerical_eos: 10.1103/PhysRevB.90.174107. - Usage:: + Usage: eos = EOS(eos_name='murnaghan') eos_fit = eos.fit(volumes, energies) diff --git a/pymatgen/analysis/ferroelectricity/polarization.py b/pymatgen/analysis/ferroelectricity/polarization.py index 349fc133827..39f60890a7e 100644 --- a/pymatgen/analysis/ferroelectricity/polarization.py +++ b/pymatgen/analysis/ferroelectricity/polarization.py @@ -424,7 +424,10 @@ class EnergyTrend: """Class for fitting trends to energies.""" def __init__(self, energies): - """:param energies: Energies""" + """ + Args: + energies: Energies + """ self.energies = energies def spline(self): diff --git a/pymatgen/analysis/fragmenter.py b/pymatgen/analysis/fragmenter.py index 8ef04f8852b..e7fc4856c4f 100644 --- a/pymatgen/analysis/fragmenter.py +++ b/pymatgen/analysis/fragmenter.py @@ -4,6 +4,7 @@ import copy import logging +from typing import TYPE_CHECKING from monty.json import MSONable @@ -11,6 +12,9 @@ from pymatgen.analysis.local_env import OpenBabelNN, metal_edge_extender from pymatgen.io.babel import BabelMolAdaptor +if TYPE_CHECKING: + from pymatgen.core.structure import Molecule + __author__ = "Samuel Blau" __copyright__ = "Copyright 2018, The Materials Project" __version__ = "2.0" @@ -27,14 +31,14 @@ class Fragmenter(MSONable): def __init__( self, - molecule, - edges=None, - depth=1, - open_rings=False, - use_metal_edge_extender=False, - opt_steps=10000, - prev_unique_frag_dict=None, - assume_previous_thoroughness=True, + molecule: Molecule, + edges: list | None = None, + depth: int = 1, + open_rings: bool = False, + use_metal_edge_extender: bool = False, + opt_steps: int = 10000, + prev_unique_frag_dict: dict | None = None, + assume_previous_thoroughness: bool = True, ): """ Standard constructor for molecule fragmentation. @@ -78,8 +82,8 @@ def __init__( if edges is None: self.mol_graph = MoleculeGraph.with_local_env_strategy(molecule, OpenBabelNN()) else: - edges = {(e[0], e[1]): None for e in edges} - self.mol_graph = MoleculeGraph.with_edges(molecule, edges) + _edges: dict[tuple[int, int], dict | None] = {(edge[0], edge[1]): None for edge in edges} + self.mol_graph = MoleculeGraph.with_edges(molecule, _edges) if ("Li" in molecule.composition or "Mg" in molecule.composition) and use_metal_edge_extender: self.mol_graph = metal_edge_extender(self.mol_graph) @@ -159,7 +163,7 @@ def __init__( for frag_key in self.unique_frag_dict: self.total_unique_fragments += len(self.unique_frag_dict[frag_key]) - def _fragment_one_level(self, old_frag_dict): + def _fragment_one_level(self, old_frag_dict: dict) -> dict: """ Perform one step of iterative fragmentation on a list of molecule graphs. Loop through the graphs, then loop through each graph's edges and attempt to remove that edge in order to obtain two @@ -210,7 +214,7 @@ def _fragment_one_level(self, old_frag_dict): new_frag_dict[new_frag_key] = [fragment] return new_frag_dict - def _open_all_rings(self): + def _open_all_rings(self) -> None: """ Having already generated all unique fragments that did not require ring opening, now we want to also obtain fragments that do require opening. We achieve this by @@ -221,7 +225,7 @@ def _open_all_rings(self): alph_formula = self.mol_graph.molecule.composition.alphabetical_formula mol_key = f"{alph_formula} E{len(self.mol_graph.graph.edges())}" self.all_unique_frag_dict[mol_key] = [self.mol_graph] - new_frag_keys = {"0": []} + new_frag_keys: dict[str, list] = {"0": []} new_frag_key_dict = {} for key in self.all_unique_frag_dict: for fragment in self.all_unique_frag_dict[key]: @@ -291,7 +295,7 @@ def _open_all_rings(self): self.all_unique_frag_dict.pop(mol_key) -def open_ring(mol_graph, bond, opt_steps): +def open_ring(mol_graph: MoleculeGraph, bond: list, opt_steps: int) -> MoleculeGraph: """ Function to actually open a ring using OpenBabel's local opt. Given a molecule graph and a bond, convert the molecule graph into an OpenBabel molecule, remove @@ -302,4 +306,5 @@ def open_ring(mol_graph, bond, opt_steps): ob_mol = BabelMolAdaptor.from_molecule_graph(mol_graph) ob_mol.remove_bond(bond[0][0] + 1, bond[0][1] + 1) ob_mol.localopt(steps=opt_steps, forcefield="uff") + return MoleculeGraph.with_local_env_strategy(ob_mol.pymatgen_mol, OpenBabelNN()) diff --git a/pymatgen/analysis/functional_groups.py b/pymatgen/analysis/functional_groups.py index 7242d13aa78..7c5021337a0 100644 --- a/pymatgen/analysis/functional_groups.py +++ b/pymatgen/analysis/functional_groups.py @@ -35,11 +35,11 @@ def __init__(self, molecule, optimize=False): """ Instantiation method for FunctionalGroupExtractor. - :param molecule: Either a filename, a pymatgen.core.structure.Molecule - object, or a pymatgen.analysis.graphs.MoleculeGraph object. - :param optimize: Default False. If True, then the input molecule will be - modified, adding Hydrogens, performing a simple conformer search, - etc. + Args: + molecule: Either a filename, a pymatgen.core.structure.Molecule + object, or a pymatgen.analysis.graphs.MoleculeGraph object. + optimize: Default False. If True, then the input molecule will be + modified, adding Hydrogens, performing a simple conformer search, etc. """ self.molgraph = None @@ -99,7 +99,8 @@ def get_heteroatoms(self, elements=None): Identify non-H, non-C atoms in the MoleculeGraph, returning a list of their node indices. - :param elements: List of elements to identify (if only certain + Args: + elements: List of elements to identify (if only certain functional groups are of interest). Returns: @@ -129,9 +130,10 @@ def get_special_carbon(self, elements=None): nitrogens or sulfurs; these O, N or S atoms must have only single bonds - all atoms in oxirane, aziridine and thiirane rings" - :param elements: List of elements that will qualify a carbon as special - (if only certain functional groups are of interest). - Default None. + Args: + elements: List of elements that will qualify a carbon as special + (if only certain functional groups are of interest). + Default None. Returns: set of ints representing node indices @@ -196,8 +198,9 @@ def link_marked_atoms(self, atoms): and attempt to connect them, returning a list of disjoint groups of special atoms (and their connected hydrogens). - :param atoms: set of marked "interesting" atoms, presumably identified - using other functions in this class. + Args: + atoms: set of marked "interesting" atoms, presumably identified + using other functions in this class. Returns: list of sets of ints, representing groups of connected atoms @@ -232,9 +235,10 @@ def get_basic_functional_groups(self, func_groups=None): TODO: Think of other functional groups that are important enough to be added (ex: do we need ethyl, butyl, propyl?) - :param func_groups: List of strs representing the functional groups of - interest. Default to None, meaning that all of the functional groups - defined in this function will be sought. + Args: + func_groups: List of strs representing the functional groups of + interest. Default to None, meaning that all of the functional groups + defined in this function will be sought. Returns: list of sets of ints, representing groups of connected atoms @@ -293,14 +297,15 @@ def get_all_functional_groups(self, elements=None, func_groups=None, catch_basic Identify all functional groups (or all within a certain subset) in the molecule, combining the methods described above. - :param elements: List of elements that will qualify a carbon as special - (if only certain functional groups are of interest). - Default None. - :param func_groups: List of strs representing the functional groups of - interest. Default to None, meaning that all of the functional groups - defined in this function will be sought. - :param catch_basic: bool. If True, use get_basic_functional_groups and - other methods + Args: + elements: List of elements that will qualify a carbon as special + (if only certain functional groups are of interest). + Default None. + func_groups: List of strs representing the functional groups of + interest. Default to None, meaning that all of the functional groups + defined in this function will be sought. + catch_basic: bool. If True, use get_basic_functional_groups and + other methods Returns: list of sets of ints, representing groups of connected atoms @@ -318,7 +323,8 @@ def categorize_functional_groups(self, groups): """ Determine classes of functional groups present in a set. - :param groups: Set of functional groups. + Args: + groups: Set of functional groups. Returns: dict containing representations of the groups, the indices of diff --git a/pymatgen/analysis/gb/grain.py b/pymatgen/analysis/gb/grain.py index 7d4e662e3bf..eb28ff894ef 100644 --- a/pymatgen/analysis/gb/grain.py +++ b/pymatgen/analysis/gb/grain.py @@ -2188,7 +2188,7 @@ def reduce_mat(mat, mag, r_matrix): mat (3 by 3 array): input matrix mag (int): reduce times for the determinant r_matrix (3 by 3 array): rotation matrix - Return: + Returns: the reduced integer array """ max_j = abs(int(round(np.linalg.det(mat) / mag))) @@ -2221,7 +2221,7 @@ def vec_to_surface(vec): Args: vec (1 by 3 array float vector): input float vector - Return: + Returns: the surface miller index of the input vector. """ miller = [None] * 3 @@ -2260,7 +2260,7 @@ def fix_pbc(structure, matrix=None): matrix (lattice matrix, 3 by 3 array/matrix): new structure's lattice matrix, If None, use input structure's matrix. - Return: + Returns: new structure with fixed frac_coords and lattice matrix """ spec = [] @@ -2288,7 +2288,7 @@ def symm_group_cubic(mat): Args: matrix (lattice matrix, n by 3 array/matrix) - Return: + Returns: cubic symmetric equivalents of the list of vectors. """ sym_group = np.zeros([24, 3, 3]) diff --git a/pymatgen/analysis/graphs.py b/pymatgen/analysis/graphs.py index b8d4ec7f88c..75716955e85 100644 --- a/pymatgen/analysis/graphs.py +++ b/pymatgen/analysis/graphs.py @@ -11,7 +11,7 @@ from itertools import combinations from operator import itemgetter from shutil import which -from typing import Any, Callable +from typing import TYPE_CHECKING, Any, Callable, cast import networkx as nx import networkx.algorithms.isomorphism as iso @@ -32,6 +32,16 @@ except ImportError: igraph = None +if TYPE_CHECKING: + from collections.abc import Sequence + + from igraph import Graph + from numpy.typing import ArrayLike + from typing_extensions import Self + + from pymatgen.analysis.local_env import NearNeighbors + from pymatgen.core import Species + logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) @@ -46,12 +56,12 @@ ConnectedSite = namedtuple("ConnectedSite", "site, jimage, index, weight, dist") -def _compare(g1, g2, i1, i2): +def _compare(g1, g2, i1, i2) -> bool: """Helper function called by isomorphic to ensure comparison of node identities.""" return g1.vs[i1]["species"] == g2.vs[i2]["species"] -def _igraph_from_nxgraph(graph): +def _igraph_from_nxgraph(graph) -> Graph: """Helper function that converts a networkx graph object into an igraph graph object.""" nodes = graph.nodes(data=True) new_igraph = igraph.Graph() @@ -63,7 +73,7 @@ def _igraph_from_nxgraph(graph): def _isomorphic(frag1: nx.Graph, frag2: nx.Graph) -> bool: """ - Internal function to check if two graph objects are isomorphic, using igraph if + Helper function to check if two graph objects are isomorphic, using igraph if if is available and networkx if it is not. """ f1_nodes = frag1.nodes(data=True) @@ -103,7 +113,7 @@ class StructureGraph(MSONable): any kind of information that connects two Sites. """ - def __init__(self, structure: Structure, graph_data=None): + def __init__(self, structure: Structure, graph_data: dict | None = None) -> None: """ If constructing this class manually, use the with_empty_graph method or with_local_env_strategy method (using an algorithm provided by the local_env @@ -149,7 +159,7 @@ def with_empty_graph( name: str = "bonds", edge_weight_name: str | None = None, edge_weight_units: str | None = None, - ) -> StructureGraph: + ) -> Self: """ Constructor for an empty StructureGraph, i.e. no edges, containing only nodes corresponding to sites in Structure. @@ -187,17 +197,18 @@ def with_empty_graph( return cls(structure, graph_data=graph_data) @staticmethod - def with_edges(structure, edges): + def with_edges(structure: Structure, edges: dict) -> StructureGraph: """ Constructor for MoleculeGraph, using pre-existing or pre-defined edges with optional edge parameters. - :param molecule: Molecule object - :param edges: dict representing the bonds of the functional - group (format: {(from_index, to_index, from_image, to_image): props}, - where props is a dictionary of properties, including weight. - Props should be None if no additional properties are to be - specified. + Args: + structure: Structure object + edges: dict representing the bonds of the functional + group (format: {(from_index, to_index, from_image, to_image): props}, + where props is a dictionary of properties, including weight. + Props should be None if no additional properties are to be + specified. Returns: sg, a StructureGraph @@ -240,17 +251,18 @@ def with_edges(structure, edges): return sg @staticmethod - def with_local_env_strategy(structure, strategy, weights=False, edge_properties=False): + def with_local_env_strategy( + structure: Structure, strategy: NearNeighbors, weights: bool = False, edge_properties: bool = False + ) -> StructureGraph: """ Constructor for StructureGraph, using a strategy from pymatgen.analysis.local_env. - :param structure: Structure object - :param strategy: an instance of a - pymatgen.analysis.local_env.NearNeighbors object - :param weights: if True, use weights from local_env class - (consult relevant class for their meaning) - :param edge_properties: if True, edge_properties from neighbors will be used + Args: + structure: Structure object + strategy: an instance of a pymatgen.analysis.local_env.NearNeighbors object + weights(bool): if True, use weights from local_env class (consult relevant class for their meaning) + edge_properties(bool): if True, edge_properties from neighbors will be used """ if not strategy.structures_allowed: raise ValueError("Chosen strategy is not designed for use with structures! Please choose another strategy.") @@ -276,12 +288,12 @@ def with_local_env_strategy(structure, strategy, weights=False, edge_properties= return sg @property - def name(self): + def name(self) -> str: """Name of graph""" return self.graph.graph["name"] @property - def edge_weight_name(self): + def edge_weight_name(self) -> str: """Name of the edge weight property of graph""" return self.graph.graph["edge_weight_name"] @@ -292,14 +304,14 @@ def edge_weight_unit(self): def add_edge( self, - from_index, - to_index, - from_jimage=(0, 0, 0), - to_jimage=None, - weight=None, - warn_duplicates=True, - edge_properties=None, - ): + from_index: int, + to_index: int, + from_jimage: tuple[int, int, int] = (0, 0, 0), + to_jimage: tuple[int, int, int] | None = None, + weight: float | None = None, + warn_duplicates: bool = True, + edge_properties: dict | None = None, + ) -> None: """ Add edge to graph. @@ -310,17 +322,18 @@ def add_edge( However, images will always be shifted so that from_index < to_index and from_jimage becomes (0, 0, 0). - :param from_index: index of site connecting from - :param to_index: index of site connecting to - :param from_jimage (tuple of ints): lattice vector of periodic - image, e.g. (1, 0, 0) for periodic image in +x direction - :param to_jimage (tuple of ints): lattice vector of image - :param weight (float): e.g. bond length - :param warn_duplicates (bool): if True, will warn if - trying to add duplicate edges (duplicate edges will not - be added in either case) - :param edge_properties (dict): any other information to - store on graph edges, similar to Structure's site_properties + Args: + from_index: index of site connecting from + to_index: index of site connecting to + from_jimage (tuple of ints): lattice vector of periodic + image, e.g. (1, 0, 0) for periodic image in +x direction + to_jimage (tuple of ints): lattice vector of image + weight (float): e.g. bond length + warn_duplicates (bool): if True, will warn if + trying to add duplicate edges (duplicate edges will not + be added in either case) + edge_properties (dict): any other information to + store on graph edges, similar to Structure's site_properties """ # this is not necessary for the class to work, but # just makes it neater @@ -369,7 +382,7 @@ def add_edge( return # sanitize types - from_jimage, to_jimage = tuple(map(int, from_jimage)), tuple(map(int, to_jimage)) + from_jimage, to_jimage = tuple(map(int, from_jimage)), tuple(map(int, to_jimage)) # type: ignore[assignment] from_index, to_index = int(from_index), int(to_index) # if edge is from site i to site i, constrain direction of edge @@ -386,7 +399,7 @@ def add_edge( if not is_positive: # let's flip the jimage, # e.g. (0, 1, 0) is equivalent to (0, -1, 0) in this case - to_jimage = tuple(-idx for idx in to_jimage) + to_jimage = tuple(-idx for idx in to_jimage) # type: ignore[assignment] # check we're not trying to add a duplicate edge # there should only ever be at most one edge @@ -420,28 +433,29 @@ def add_edge( def insert_node( self, - idx, - species, - coords, - coords_are_cartesian=False, - validate_proximity=False, - site_properties=None, - edges=None, - ): + idx: int, + species: Species, + coords: ArrayLike, + coords_are_cartesian: bool = False, + validate_proximity: bool = False, + site_properties: dict | None = None, + edges: list | dict | None = None, + ) -> None: """ A wrapper around Molecule.insert(), which also incorporates the new site into the MoleculeGraph. - :param idx: Index at which to insert the new site - :param species: Species for the new site - :param coords: 3x1 array representing coordinates of the new site - :param coords_are_cartesian: Whether coordinates are cartesian. - Defaults to False. - :param validate_proximity: For Molecule.insert(); if True (default - False), distance will be checked to ensure that site can be safely - added. - :param site_properties: Site properties for Molecule - :param edges: List of dicts representing edges to be added to the + Args: + idx: Index at which to insert the new site + species: Species for the new site + coords: 3x1 array representing coordinates of the new site + coords_are_cartesian: Whether coordinates are cartesian. + Defaults to False. + validate_proximity: For Molecule.insert(); if True (default + False), distance will be checked to ensure that + site can be safely added. + site_properties: Site properties for Molecule + edges: List of dicts representing edges to be added to the MoleculeGraph. These edges must include the index of the new site i, and all indices used for these edges should reflect the MoleculeGraph AFTER the insertion, NOT before. Each dict should at @@ -482,7 +496,7 @@ def insert_node( except KeyError: raise RuntimeError("Some edges are invalid.") - def set_node_attributes(self): + def set_node_attributes(self) -> None: """ Gives each node a "specie" and a "coords" attribute, updated with the current species and coordinates. @@ -501,27 +515,28 @@ def set_node_attributes(self): def alter_edge( self, - from_index, - to_index, - to_jimage=None, - new_weight=None, - new_edge_properties=None, + from_index: int, + to_index: int, + to_jimage: tuple | None = None, + new_weight: float | None = None, + new_edge_properties: dict | None = None, ): """ Alters either the weight or the edge_properties of an edge in the StructureGraph. - :param from_index: int - :param to_index: int - :param to_jimage: tuple - :param new_weight: alter_edge does not require - that weight be altered. As such, by default, this - is None. If weight is to be changed, it should be a - float. - :param new_edge_properties: alter_edge does not require - that edge_properties be altered. As such, by default, - this is None. If any edge properties are to be changed, - it should be a dictionary of edge properties to be changed. + Args: + from_index: int + to_index: int + to_jimage: tuple + new_weight: alter_edge does not require + that weight be altered. As such, by default, this + is None. If weight is to be changed, it should be a + float. + new_edge_properties: alter_edge does not require + that edge_properties be altered. As such, by default, + this is None. If any edge properties are to be changed, + it should be a dictionary of edge properties to be changed. """ existing_edges = self.graph.get_edge_data(from_index, to_index) @@ -545,16 +560,19 @@ def alter_edge( for prop in list(new_edge_properties): self.graph[from_index][to_index][edge_index][prop] = new_edge_properties[prop] - def break_edge(self, from_index, to_index, to_jimage=None, allow_reverse=False): + def break_edge( + self, from_index: int, to_index: int, to_jimage: tuple | None = None, allow_reverse: bool = False + ) -> None: """ Remove an edge from the StructureGraph. If no image is given, this method will fail. - :param from_index: int - :param to_index: int - :param to_jimage: tuple - :param allow_reverse: If allow_reverse is True, then break_edge will - attempt to break both (from_index, to_index) and, failing that, - will attempt to break (to_index, from_index). + Args: + from_index: int + to_index: int + to_jimage: tuple + allow_reverse: If allow_reverse is True, then break_edge will + attempt to break both (from_index, to_index) and, failing that, + will attempt to break (to_index, from_index). """ # ensure that edge exists before attempting to remove it existing_edges = self.graph.get_edge_data(from_index, to_index) @@ -586,12 +604,13 @@ def break_edge(self, from_index, to_index, to_jimage=None, allow_reverse=False): f"no edge exists between those sites." ) - def remove_nodes(self, indices): + def remove_nodes(self, indices: Sequence[int | None]) -> None: """ A wrapper for Molecule.remove_sites(). - :param indices: list of indices in the current Molecule (and graph) to - be removed. + Args: + indices: list of indices in the current Molecule (and graph) to + be removed. """ self.structure.remove_sites(indices) self.graph.remove_nodes_from(indices) @@ -603,12 +622,12 @@ def remove_nodes(self, indices): def substitute_group( self, - index, - func_grp, - strategy, - bond_order=1, - graph_dict=None, - strategy_params=None, + index: int, + func_grp: Molecule | str, + strategy: Any, + bond_order: int = 1, + graph_dict: dict | None = None, + strategy_params: dict | None = None, ): """ Builds off of Structure.substitute to replace an atom in self.structure @@ -619,34 +638,34 @@ def substitute_group( substituted will not place atoms to close to each other, or violate the dimensions of the Lattice. - :param index: Index of atom to substitute. - :param func_grp: Substituent molecule. There are two options: - - 1. Providing an actual Molecule as the input. The first atom - must be a DummySpecies X, indicating the position of - nearest neighbor. The second atom must be the next - nearest atom. For example, for a methyl group - substitution, func_grp should be X-CH3, where X is the - first site and C is the second site. What the code will - do is to remove the index site, and connect the nearest - neighbor to the C atom in CH3. The X-C bond indicates the - directionality to connect the atoms. - 2. A string name. The molecule will be obtained from the - relevant template in func_groups.json. - :param strategy: Class from pymatgen.analysis.local_env. - :param bond_order: A specified bond order to calculate the bond - length between the attached functional group and the nearest - neighbor site. Defaults to 1. - :param graph_dict: Dictionary representing the bonds of the functional - group (format: {(u, v): props}, where props is a dictionary of - properties, including weight. If None, then the algorithm - will attempt to automatically determine bonds using one of - a list of strategies defined in pymatgen.analysis.local_env. - :param strategy_params: dictionary of keyword arguments for strategy. - If None, default parameters will be used. + Args: + index: Index of atom to substitute. + func_grp: Substituent molecule. There are two options: + 1. Providing an actual Molecule as the input. The first atom + must be a DummySpecies X, indicating the position of + nearest neighbor. The second atom must be the next + nearest atom. For example, for a methyl group + substitution, func_grp should be X-CH3, where X is the + first site and C is the second site. What the code will + do is to remove the index site, and connect the nearest + neighbor to the C atom in CH3. The X-C bond indicates the + directionality to connect the atoms. + 2. A string name. The molecule will be obtained from the + relevant template in func_groups.json. + strategy: Class from pymatgen.analysis.local_env. + bond_order: A specified bond order to calculate the bond + length between the attached functional group and the nearest + neighbor site. Defaults to 1. + graph_dict: Dictionary representing the bonds of the functional + group (format: {(u, v): props}, where props is a dictionary of + properties, including weight. If None, then the algorithm + will attempt to automatically determine bonds using one of + a list of strategies defined in pymatgen.analysis.local_env. + strategy_params: dictionary of keyword arguments for strategy. + If None, default parameters will be used. """ - def map_indices(grp): + def map_indices(grp: Molecule) -> dict[int, int]: grp_map = {} # Get indices now occupied by functional group @@ -706,15 +725,17 @@ def map_indices(grp): warn_duplicates=False, ) - def get_connected_sites(self, n, jimage=(0, 0, 0)): + def get_connected_sites(self, n: int, jimage: tuple[int, int, int] = (0, 0, 0)) -> list[ConnectedSite]: """ Returns a named tuple of neighbors of site n: periodic_site, jimage, index, weight. Index is the index of the corresponding site in the original structure, weight can be None if not defined. - :param n: index of Site in Structure - :param jimage: lattice vector of site + + Args: + n: index of Site in Structure + jimage: lattice vector of site Returns: list of ConnectedSite tuples, @@ -726,8 +747,8 @@ def get_connected_sites(self, n, jimage=(0, 0, 0)): out_edges = [(u, v, d, "out") for u, v, d in self.graph.out_edges(n, data=True)] in_edges = [(u, v, d, "in") for u, v, d in self.graph.in_edges(n, data=True)] - for u, v, d, dir in out_edges + in_edges: - to_jimage = d["to_jimage"] + for u, v, data, dir in out_edges + in_edges: + to_jimage = data["to_jimage"] if dir == "in": u, v = v, u @@ -740,9 +761,10 @@ def get_connected_sites(self, n, jimage=(0, 0, 0)): # from_site if jimage arg != (0, 0, 0) relative_jimage = np.subtract(to_jimage, jimage) - dist = self.structure[u].distance(self.structure[v], jimage=relative_jimage) + u_site = cast(PeriodicSite, self.structure[u]) # tell mypy that u_site is a PeriodicSite + dist = u_site.distance(self.structure[v], jimage=relative_jimage) - weight = d.get("weight") + weight = data.get("weight") if (v, to_jimage) not in connected_site_images: connected_site = ConnectedSite(site=site, jimage=to_jimage, index=v, weight=weight, dist=dist) @@ -751,35 +773,38 @@ def get_connected_sites(self, n, jimage=(0, 0, 0)): connected_site_images.add((v, to_jimage)) # return list sorted by closest sites first - connected_sites = list(connected_sites) - connected_sites.sort(key=lambda x: x.dist) + _connected_sites = list(connected_sites) + _connected_sites.sort(key=lambda x: x.dist) - return connected_sites + return _connected_sites - def get_coordination_of_site(self, n): + def get_coordination_of_site(self, n: int) -> int: """ - Returns the number of neighbors of site n. - In graph terms, simply returns degree - of node corresponding to site n. - :param n: index of site - :return (int): + Returns the number of neighbors of site n. In graph terms, + simply returns degree of node corresponding to site n. + + Args: + n: index of site + + Returns: + (int): number of neighbors of site n. """ n_self_loops = sum(1 for n, v in self.graph.edges(n) if n == v) return self.graph.degree(n) - n_self_loops def draw_graph_to_file( self, - filename="graph", - diff=None, - hide_unconnected_nodes=False, - hide_image_edges=True, - edge_colors=False, - node_labels=False, - weight_labels=False, - image_labels=False, - color_scheme="VESTA", - keep_dot=False, - algo="fdp", + filename: str = "graph", + diff: StructureGraph = None, + hide_unconnected_nodes: bool = False, + hide_image_edges: bool = True, + edge_colors: bool = False, + node_labels: bool = False, + weight_labels: bool = False, + image_labels: bool = False, + color_scheme: str = "VESTA", + keep_dot: bool = False, + algo: str = "fdp", ): """ Draws graph using GraphViz. @@ -793,31 +818,27 @@ def draw_graph_to_file( `hide_image_edges` can help, especially in larger graphs. - :param filename: filename to output, will detect filetype - from extension (any graphviz filetype supported, such as - pdf or png) - :param diff (StructureGraph): an additional graph to - compare with, will color edges red that do not exist in diff - and edges green that are in diff graph but not in the - reference graph - :param hide_unconnected_nodes: if True, hide unconnected - nodes - :param hide_image_edges: if True, do not draw edges that - go through periodic boundaries - :param edge_colors (bool): if True, use node colors to - color edges - :param node_labels (bool): if True, label nodes with - species and site index - :param weight_labels (bool): if True, label edges with - weights - :param image_labels (bool): if True, label edges with - their periodic images (usually only used for debugging, - edges to periodic images always appear as dashed lines) - :param color_scheme (str): "VESTA" or "JMOL" - :param keep_dot (bool): keep GraphViz .dot file for later - visualization - :param algo: any graphviz algo, "neato" (for simple graphs) - or "fdp" (for more crowded graphs) usually give good outputs + Args: + filename: filename to output, will detect filetype + from extension (any graphviz filetype supported, such as + pdf or png) + diff (StructureGraph): an additional graph to + compare with, will color edges red that do not exist in diff + and edges green that are in diff graph but not in the + reference graph + hide_unconnected_nodes: if True, hide unconnected nodes + hide_image_edges: if True, do not draw edges that + go through periodic boundaries + edge_colors (bool): if True, use node colors to color edges + node_labels (bool): if True, label nodes with species and site index + weight_labels (bool): if True, label edges with weights + image_labels (bool): if True, label edges with + their periodic images (usually only used for debugging, + edges to periodic images always appear as dashed lines) + color_scheme (str): "VESTA" or "JMOL" + keep_dot (bool): keep GraphViz .dot file for later visualization + algo: any graphviz algo, "neato" (for simple graphs) + or "fdp" (for more crowded graphs) usually give good outputs """ if not which(algo): raise RuntimeError("StructureGraph graph drawing requires GraphViz binaries to be in the path.") @@ -907,14 +928,14 @@ def draw_graph_to_file( # optionally highlight differences with another graph if diff: - diff = self.diff(diff, strict=True) + _diff = self.diff(diff, strict=True) green_edges = [] red_edges = [] for u, v, k, d in g.edges(keys=True, data=True): - if (u, v, d["to_jimage"]) in diff["self"]: + if (u, v, d["to_jimage"]) in _diff["self"]: # edge has been deleted red_edges.append((u, v, k)) - elif (u, v, d["to_jimage"]) in diff["other"]: + elif (u, v, d["to_jimage"]) in _diff["other"]: # edge has been added green_edges.append((u, v, k)) for u, v, k in green_edges: @@ -938,7 +959,7 @@ def draw_graph_to_file( os.remove(f"{basename}.dot") @property - def types_and_weights_of_connections(self): + def types_and_weights_of_connections(self) -> dict: """ Extract a dictionary summarizing the types and weights of edges in the graph. @@ -963,7 +984,7 @@ def get_label(u, v): return dict(types) @property - def weight_statistics(self): + def weight_statistics(self) -> dict: """ Extract a statistical summary of edge weights present in the graph. @@ -983,17 +1004,16 @@ def weight_statistics(self): "variance": stats.variance, } - def types_of_coordination_environments(self, anonymous=False): + def types_of_coordination_environments(self, anonymous: bool = False) -> list[str]: """ Extract information on the different co-ordination environments present in the graph. - :param anonymous: if anonymous, will replace specie names - with A, B, C, etc. + Args: + anonymous: if anonymous, will replace specie names with A, B, C, etc. Returns: - a list of co-ordination environments, - e.g. ['Mo-S(6)', 'S-Mo(3)'] + List of coordination environments, e.g. {'Mo-S(6)', 'S-Mo(3)'} """ motifs = set() for idx, site in enumerate(self.structure): @@ -1002,30 +1022,30 @@ def types_of_coordination_environments(self, anonymous=False): connected_sites = self.get_connected_sites(idx) connected_species = [connected_site.site.species_string for connected_site in connected_sites] - labels = [] + sp_counts = [] for sp in set(connected_species): count = connected_species.count(sp) - labels.append((count, sp)) + sp_counts.append((count, sp)) - labels = sorted(labels, reverse=True) + sp_counts = sorted(sp_counts, reverse=True) if anonymous: mapping = {centre_sp: "A"} available_letters = [chr(66 + idx) for idx in range(25)] - for label in labels: + for label in sp_counts: sp = label[1] if sp not in mapping: mapping[sp] = available_letters.pop(0) centre_sp = "A" - labels = [(label[0], mapping[label[1]]) for label in labels] + sp_counts = [(label[0], mapping[label[1]]) for label in sp_counts] - labels = [f"{label[1]}({label[0]})" for label in labels] + labels = [f"{label[1]}({label[0]})" for label in sp_counts] motif = f"{centre_sp}-{','.join(labels)}" motifs.add(motif) - return sorted(motifs) + return sorted(set(motifs)) - def as_dict(self): + def as_dict(self) -> dict: """ As in pymatgen.core.Structure except with using `to_dict_of_dicts` from NetworkX @@ -1039,7 +1059,7 @@ def as_dict(self): } @classmethod - def from_dict(cls, d): + def from_dict(cls, d) -> Self: """As in pymatgen.core.Structure except restoring graphs using from_dict_of_dicts from NetworkX to restore graph information. """ @@ -1055,7 +1075,9 @@ def __mul__(self, scaling_matrix): graph could also be done on the original graph, but a larger graph can be easier to visualize and reason about. - :param scaling_matrix: same as Structure.__mul__ + + Args: + scaling_matrix: same as Structure.__mul__ """ # Developer note: a different approach was also trialed, using # a simple Graph (instead of MultiDiGraph), with node indices @@ -1299,7 +1321,7 @@ def __len__(self): """length of Structure / number of nodes in graph""" return len(self.structure) - def sort(self, key=None, reverse=False): + def sort(self, key=None, reverse: bool = False) -> None: """Same as Structure.sort(). Also remaps nodes in graph. Args: @@ -1340,8 +1362,8 @@ def __eq__(self, other: object) -> bool: and have the same edges between Sites. Edge weights can be different and StructureGraphs can still be considered equal. - :param other: StructureGraph - :return (bool): + Args: + other: StructureGraph """ if not isinstance(other, StructureGraph): return NotImplemented @@ -1357,7 +1379,7 @@ def __eq__(self, other: object) -> bool: return (edges == edges_other) and (self.structure == other_sorted.structure) - def diff(self, other, strict=True): + def diff(self, other: StructureGraph, strict: bool = True) -> dict: """ Compares two StructureGraphs. Returns dict with keys 'self', 'other', 'both' with edges that are @@ -1377,14 +1399,15 @@ def diff(self, other, strict=True): same if the underlying Structures are ordered differently. - :param other: StructureGraph - :param strict: if False, will compare bonds - from different Structures, with node indices - replaced by Species strings, will not count - number of occurrences of bonds + Args: + other: StructureGraph + strict: if False, will compare bonds + from different Structures, with node indices + replaced by Species strings, will not count + number of occurrences of bonds """ if self.structure != other.structure and strict: - return ValueError("Meaningless to compare StructureGraphs if corresponding Structures are different.") + raise ValueError("Meaningless to compare StructureGraphs if corresponding Structures are different.") if strict: # sort for consistent node indices @@ -1393,25 +1416,27 @@ def diff(self, other, strict=True): other_sorted = copy.copy(other) other_sorted.sort(key=lambda site: mapping[tuple(site.frac_coords)]) - edges = {(u, v, d["to_jimage"]) for u, v, d in self.graph.edges(keys=False, data=True)} + edges: set[tuple] = {(u, v, d["to_jimage"]) for u, v, d in self.graph.edges(keys=False, data=True)} - edges_other = {(u, v, d["to_jimage"]) for u, v, d in other_sorted.graph.edges(keys=False, data=True)} + edges_other: set[tuple] = { + (u, v, d["to_jimage"]) for u, v, d in other_sorted.graph.edges(keys=False, data=True) + } else: edges = { (str(self.structure[u].specie), str(self.structure[v].specie)) - for u, v, d in self.graph.edges(keys=False, data=True) + for u, v, _d in self.graph.edges(keys=False, data=True) } edges_other = { (str(other.structure[u].specie), str(other.structure[v].specie)) - for u, v, d in other.graph.edges(keys=False, data=True) + for u, v, _d in other.graph.edges(keys=False, data=True) } if len(edges) == 0 and len(edges_other) == 0: - jaccard_dist = 0 # by definition + jaccard_dist = 0.0 # by definition else: - jaccard_dist = 1 - len(edges & edges_other) / len(edges | edges_other) + jaccard_dist = 1.0 - len(edges & edges_other) / len(edges | edges_other) return { "self": edges - edges_other, @@ -1420,7 +1445,7 @@ def diff(self, other, strict=True): "dist": jaccard_dist, } - def get_subgraphs_as_molecules(self, use_weights=False): + def get_subgraphs_as_molecules(self, use_weights: bool = False) -> list[Molecule]: """ Retrieve subgraphs as molecules, useful for extracting molecules from periodic crystals. @@ -1429,12 +1454,13 @@ def get_subgraphs_as_molecules(self, use_weights=False): present in the crystal (a duplicate defined as an isomorphic subgraph). - :param use_weights (bool): If True, only treat subgraphs - as isomorphic if edges have the same weights. Typically, - this means molecules will need to have the same bond - lengths to be defined as duplicates, otherwise bond - lengths can differ. This is a fairly robust approach, - but will treat e.g. enantiomers as being duplicates. + Args: + use_weights (bool): If True, only treat subgraphs + as isomorphic if edges have the same weights. Typically, + this means molecules will need to have the same bond + lengths to be defined as duplicates, otherwise bond + lengths can differ. This is a fairly robust approach, + but will treat e.g. enantiomers as being duplicates. Returns: list of unique Molecules in Structure @@ -1474,7 +1500,7 @@ def edge_match(e1, e2): return True # prune duplicate subgraphs - unique_subgraphs = [] + unique_subgraphs: list = [] for subgraph in molecule_subgraphs: already_present = [ nx.is_isomorphic(subgraph, g, node_match=node_match, edge_match=edge_match) for g in unique_subgraphs @@ -1531,11 +1557,12 @@ def __init__(self, molecule, graph_data=None): Use cases for this include storing bonding information, NMR J-couplings, Heisenberg exchange parameters, etc. - :param molecule: Molecule object + Args: + molecule: Molecule object - :param graph_data: dict containing graph information in - dict format (not intended to be constructed manually, - see as_dict method for format) + graph_data: dict containing graph information in + dict format (not intended to be constructed manually, + see as_dict method for format) """ if isinstance(molecule, MoleculeGraph): # just make a copy from input @@ -1560,19 +1587,22 @@ def __init__(self, molecule, graph_data=None): self.set_node_attributes() @classmethod - def with_empty_graph(cls, molecule, name="bonds", edge_weight_name=None, edge_weight_units=None): + def with_empty_graph(cls, molecule, name="bonds", edge_weight_name=None, edge_weight_units=None) -> Self: """ Constructor for MoleculeGraph, returns a MoleculeGraph object with an empty graph (no edges, only nodes defined that correspond to Sites in Molecule). - :param molecule (Molecule): - :param name (str): name of graph, e.g. "bonds" - :param edge_weight_name (str): name of edge weights, - e.g. "bond_length" or "exchange_constant" - :param edge_weight_units (str): name of edge weight units + Args: + molecule (Molecule): + name (str): name of graph, e.g. "bonds" + edge_weight_name (str): name of edge weights, + e.g. "bond_length" or "exchange_constant" + edge_weight_units (str): name of edge weight units e.g. "Å" or "eV" - :return (MoleculeGraph): + + Returns: + MoleculeGraph """ if edge_weight_name and (edge_weight_units is None): raise ValueError( @@ -1597,19 +1627,20 @@ def with_empty_graph(cls, molecule, name="bonds", edge_weight_name=None, edge_we return cls(molecule, graph_data=graph_data) @staticmethod - def with_edges(molecule: Molecule, edges: dict[tuple[int, int], dict]): + def with_edges(molecule: Molecule, edges: dict[tuple[int, int], None | dict]) -> MoleculeGraph: """ Constructor for MoleculeGraph, using pre-existing or pre-defined edges with optional edge parameters. - :param molecule: Molecule object - :param edges: dict representing the bonds of the functional - group (format: {(u, v): props}, where props is a dictionary of - properties, including weight. Props should be None if no - additional properties are to be specified. + Args: + molecule: Molecule object + edges: dict representing the bonds of the functional + group (format: {(u, v): props}, where props is a dictionary of + properties, including weight. Props should be None if no + additional properties are to be specified. Returns: - mg, a MoleculeGraph + A MoleculeGraph """ mg = MoleculeGraph.with_empty_graph(molecule, name="bonds", edge_weight_name="weight", edge_weight_units="") @@ -1620,12 +1651,13 @@ def with_edges(molecule: Molecule, edges: dict[tuple[int, int], dict]): except TypeError: raise ValueError("Edges must be given as (from_index, to_index) tuples") - if props is not None: + if props is None: + weight = None + + else: weight = props.pop("weight", None) if len(props.items()) == 0: - props = None # type: ignore[assignment] - else: - weight = None + props = None nodes = mg.graph.nodes if not (from_index in nodes and to_index in nodes): @@ -1639,14 +1671,14 @@ def with_edges(molecule: Molecule, edges: dict[tuple[int, int], dict]): return mg @staticmethod - def with_local_env_strategy(molecule, strategy): + def with_local_env_strategy(molecule, strategy) -> MoleculeGraph: """ Constructor for MoleculeGraph, using a strategy from pymatgen.analysis.local_env. - :param molecule: Molecule object - :param strategy: an instance of a - pymatgen.analysis.local_env.NearNeighbors object + molecule: Molecule object + strategy: an instance of a + pymatgen.analysis.local_env.NearNeighbors object Returns: mg, a MoleculeGraph @@ -1736,14 +1768,15 @@ def add_edge( However, images will always be shifted so that from_index < to_index and from_jimage becomes (0, 0, 0). - :param from_index: index of site connecting from - :param to_index: index of site connecting to - :param weight (float): e.g. bond length - :param warn_duplicates (bool): if True, will warn if - trying to add duplicate edges (duplicate edges will not - be added in either case) - :param edge_properties (dict): any other information to - store on graph edges, similar to Structure's site_properties + Args: + from_index: index of site connecting from + to_index: index of site connecting to + weight (float): e.g. bond length + warn_duplicates (bool): if True, will warn if + trying to add duplicate edges (duplicate edges will not + be added in either case) + edge_properties (dict): any other information to + store on graph edges, similar to Structure's site_properties """ # this is not necessary for the class to work, but # just makes it neater @@ -1783,19 +1816,20 @@ def insert_node( A wrapper around Molecule.insert(), which also incorporates the new site into the MoleculeGraph. - :param idx: Index at which to insert the new site - :param species: Species for the new site - :param coords: 3x1 array representing coordinates of the new site - :param validate_proximity: For Molecule.insert(); if True (default - False), distance will be checked to ensure that site can be safely - added. - :param site_properties: Site properties for Molecule - :param edges: List of dicts representing edges to be added to the - MoleculeGraph. These edges must include the index of the new site i, - and all indices used for these edges should reflect the - MoleculeGraph AFTER the insertion, NOT before. Each dict should at - least have a "to_index" and "from_index" key, and can also have a - "weight" and a "properties" key. + Args: + idx: Index at which to insert the new site + species: Species for the new site + coords: 3x1 array representing coordinates of the new site + validate_proximity: For Molecule.insert(); if True (default + False), distance will be checked to ensure that site can be safely + added. + site_properties: Site properties for Molecule + edges: List of dicts representing edges to be added to the + MoleculeGraph. These edges must include the index of the new site i, + and all indices used for these edges should reflect the + MoleculeGraph AFTER the insertion, NOT before. Each dict should at + least have a "to_index" and "from_index" key, and can also have a + "weight" and a "properties" key. """ self.molecule.insert( idx, @@ -1850,16 +1884,17 @@ def alter_edge(self, from_index, to_index, new_weight=None, new_edge_properties= Alters either the weight or the edge_properties of an edge in the MoleculeGraph. - :param from_index: int - :param to_index: int - :param new_weight: alter_edge does not require - that weight be altered. As such, by default, this - is None. If weight is to be changed, it should be a - float. - :param new_edge_properties: alter_edge does not require - that edge_properties be altered. As such, by default, - this is None. If any edge properties are to be changed, - it should be a dictionary of edge properties to be changed. + Args: + from_index: int + to_index: int + new_weight: alter_edge does not require + that weight be altered. As such, by default, this + is None. If weight is to be changed, it should be a + float. + new_edge_properties: alter_edge does not require + that edge_properties be altered. As such, by default, + this is None. If any edge properties are to be changed, + it should be a dictionary of edge properties to be changed. """ existing_edge = self.graph.get_edge_data(from_index, to_index) @@ -1881,11 +1916,12 @@ def break_edge(self, from_index, to_index, allow_reverse=False): """ Remove an edge from the MoleculeGraph. - :param from_index: int - :param to_index: int - :param allow_reverse: If allow_reverse is True, then break_edge will - attempt to break both (from_index, to_index) and, failing that, - will attempt to break (to_index, from_index). + Args: + from_index: int + to_index: int + allow_reverse: If allow_reverse is True, then break_edge will + attempt to break both (from_index, to_index) and, failing that, + will attempt to break (to_index, from_index). """ # ensure that edge exists before attempting to remove it existing_edge = self.graph.get_edge_data(from_index, to_index) @@ -1906,12 +1942,12 @@ def break_edge(self, from_index, to_index, allow_reverse=False): f"no edge exists between those sites." ) - def remove_nodes(self, indices): + def remove_nodes(self, indices: list[int]) -> None: """ A wrapper for Molecule.remove_sites(). - :param indices: list of indices in the current Molecule (and graph) to - be removed. + Args: + indices: indices in the current Molecule (and graph) to be removed. """ self.molecule.remove_sites(indices) self.graph.remove_nodes_from(indices) @@ -2009,14 +2045,16 @@ def split_molecule_subgraphs(self, bonds, allow_reverse=False, alterations=None) NOTE: This function does not modify the original MoleculeGraph. It creates a copy, modifies that, and returns two or more new MoleculeGraph objects. - :param bonds: list of tuples (from_index, to_index) - representing bonds to be broken to split the MoleculeGraph. - :param alterations: a dict {(from_index, to_index): alt}, - where alt is a dictionary including weight and/or edge - properties to be changed following the split. - :param allow_reverse: If allow_reverse is True, then break_edge will - attempt to break both (from_index, to_index) and, failing that, - will attempt to break (to_index, from_index). + + Args: + bonds: list of tuples (from_index, to_index) + representing bonds to be broken to split the MoleculeGraph. + alterations: a dict {(from_index, to_index): alt}, + where alt is a dictionary including weight and/or edge + properties to be changed following the split. + allow_reverse: If allow_reverse is True, then break_edge will + attempt to break both (from_index, to_index) and, failing that, + will attempt to break (to_index, from_index). Returns: list of MoleculeGraphs. @@ -2128,31 +2166,31 @@ def substitute_group( NOTE: using a MoleculeGraph will generally produce a different graph compared with using a Molecule or str (when not using graph_dict). - :param index: Index of atom to substitute. - :param func_grp: Substituent molecule. There are three options: - - 1. Providing an actual molecule as the input. The first atom - must be a DummySpecies X, indicating the position of - nearest neighbor. The second atom must be the next - nearest atom. For example, for a methyl group - substitution, func_grp should be X-CH3, where X is the - first site and C is the second site. What the code will - do is to remove the index site, and connect the nearest - neighbor to the C atom in CH3. The X-C bond indicates the - directionality to connect the atoms. - 2. A string name. The molecule will be obtained from the - relevant template in func_groups.json. - 3. A MoleculeGraph object. - :param strategy: Class from pymatgen.analysis.local_env. - :param bond_order: A specified bond order to calculate the bond + Args: + index: Index of atom to substitute. + func_grp: Substituent molecule. There are three options: + 1. Providing an actual molecule as the input. The first atom + must be a DummySpecies X, indicating the position of + nearest neighbor. The second atom must be the next + nearest atom. For example, for a methyl group + substitution, func_grp should be X-CH3, where X is the + first site and C is the second site. What the code will + do is to remove the index site, and connect the nearest + neighbor to the C atom in CH3. The X-C bond indicates the + directionality to connect the atoms. + 2. A string name. The molecule will be obtained from the + relevant template in func_groups.json. + 3. A MoleculeGraph object. + strategy: Class from pymatgen.analysis.local_env. + bond_order: A specified bond order to calculate the bond length between the attached functional group and the nearest neighbor site. Defaults to 1. - :param graph_dict: Dictionary representing the bonds of the functional + graph_dict: Dictionary representing the bonds of the functional group (format: {(u, v): props}, where props is a dictionary of properties, including weight. If None, then the algorithm will attempt to automatically determine bonds using one of a list of strategies defined in pymatgen.analysis.local_env. - :param strategy_params: dictionary of keyword arguments for strategy. + strategy_params: dictionary of keyword arguments for strategy. If None, default parameters will be used. """ @@ -2242,32 +2280,32 @@ def replace_group( TODO: Figure out how to replace into a ring structure. - :param index: Index of atom to substitute. - :param func_grp: Substituent molecule. There are three options: - - 1. Providing an actual molecule as the input. The first atom - must be a DummySpecies X, indicating the position of - nearest neighbor. The second atom must be the next - nearest atom. For example, for a methyl group - substitution, func_grp should be X-CH3, where X is the - first site and C is the second site. What the code will - do is to remove the index site, and connect the nearest - neighbor to the C atom in CH3. The X-C bond indicates the - directionality to connect the atoms. - 2. A string name. The molecule will be obtained from the - relevant template in func_groups.json. - 3. A MoleculeGraph object. - :param strategy: Class from pymatgen.analysis.local_env. - :param bond_order: A specified bond order to calculate the bond - length between the attached functional group and the nearest - neighbor site. Defaults to 1. - :param graph_dict: Dictionary representing the bonds of the functional - group (format: {(u, v): props}, where props is a dictionary of - properties, including weight. If None, then the algorithm - will attempt to automatically determine bonds using one of - a list of strategies defined in pymatgen.analysis.local_env. - :param strategy_params: dictionary of keyword arguments for strategy. - If None, default parameters will be used. + Args: + index: Index of atom to substitute. + func_grp: Substituent molecule. There are three options: + 1. Providing an actual molecule as the input. The first atom + must be a DummySpecies X, indicating the position of + nearest neighbor. The second atom must be the next + nearest atom. For example, for a methyl group + substitution, func_grp should be X-CH3, where X is the + first site and C is the second site. What the code will + do is to remove the index site, and connect the nearest + neighbor to the C atom in CH3. The X-C bond indicates the + directionality to connect the atoms. + 2. A string name. The molecule will be obtained from the + relevant template in func_groups.json. + 3. A MoleculeGraph object. + strategy: Class from pymatgen.analysis.local_env. + bond_order: A specified bond order to calculate the bond + length between the attached functional group and the nearest + neighbor site. Defaults to 1. + graph_dict: Dictionary representing the bonds of the functional + group (format: {(u, v): props}, where props is a dictionary of + properties, including weight. If None, then the algorithm + will attempt to automatically determine bonds using one of + a list of strategies defined in pymatgen.analysis.local_env. + strategy_params: dictionary of keyword arguments for strategy. + If None, default parameters will be used. """ self.set_node_attributes() neighbors = self.get_connected_sites(index) @@ -2368,8 +2406,9 @@ def get_connected_sites(self, n): Index is the index of the corresponding site in the original structure, weight can be None if not defined. - :param n: index of Site in Molecule - :param jimage: lattice vector of site + Args: + n: index of Site in Molecule + jimage: lattice vector of site Returns: list of ConnectedSite tuples, @@ -2402,13 +2441,17 @@ def get_connected_sites(self, n): return connected_sites - def get_coordination_of_site(self, n): + def get_coordination_of_site(self, n) -> int: """ Returns the number of neighbors of site n. In graph terms, simply returns degree of node corresponding to site n. - :param n: index of site - :return (int): + + Args: + n: index of site + + Returns: + (int): the number of neighbors of site n. """ n_self_loops = sum(1 for n, v in self.graph.edges(n) if n == v) return self.graph.degree(n) - n_self_loops @@ -2439,31 +2482,28 @@ def draw_graph_to_file( `hide_image_edges` can help, especially in larger graphs. - :param filename: filename to output, will detect filetype - from extension (any graphviz filetype supported, such as - pdf or png) - :param diff (StructureGraph): an additional graph to - compare with, will color edges red that do not exist in diff - and edges green that are in diff graph but not in the - reference graph - :param hide_unconnected_nodes: if True, hide unconnected - nodes - :param hide_image_edges: if True, do not draw edges that - go through periodic boundaries - :param edge_colors (bool): if True, use node colors to - color edges - :param node_labels (bool): if True, label nodes with - species and site index - :param weight_labels (bool): if True, label edges with - weights - :param image_labels (bool): if True, label edges with - their periodic images (usually only used for debugging, - edges to periodic images always appear as dashed lines) - :param color_scheme (str): "VESTA" or "JMOL" - :param keep_dot (bool): keep GraphViz .dot file for later - visualization - :param algo: any graphviz algo, "neato" (for simple graphs) - or "fdp" (for more crowded graphs) usually give good outputs + Args: + filename: filename to output, will detect filetype + from extension (any graphviz filetype supported, such as + pdf or png) + diff (StructureGraph): an additional graph to + compare with, will color edges red that do not exist in diff + and edges green that are in diff graph but not in the + reference graph + hide_unconnected_nodes: if True, hide unconnected nodes + hide_image_edges: if True, do not draw edges that + go through periodic boundaries + edge_colors (bool): if True, use node colors to color edges + node_labels (bool): if True, label nodes with + species and site index + weight_labels (bool): if True, label edges with weights + image_labels (bool): if True, label edges with + their periodic images (usually only used for debugging, + edges to periodic images always appear as dashed lines) + color_scheme (str): "VESTA" or "JMOL" + keep_dot (bool): keep GraphViz .dot file for later visualization + algo: any graphviz algo, "neato" (for simple graphs) + or "fdp" (for more crowded graphs) usually give good outputs """ if not which(algo): raise RuntimeError("StructureGraph graph drawing requires GraphViz binaries to be in the path.") @@ -2694,9 +2734,6 @@ def __eq__(self, other: object) -> bool: Two MoleculeGraphs are equal if they have equal Molecules, and have the same edges between Sites. Edge weights can be different and MoleculeGraphs can still be considered equal. - - :param other: MoleculeGraph - :return (bool): """ if not isinstance(other, type(self)): return NotImplemented @@ -2716,13 +2753,14 @@ def __eq__(self, other: object) -> bool: return (edges == edges_other) and (self.molecule == other_sorted.molecule) - def isomorphic_to(self, other): + def isomorphic_to(self, other: MoleculeGraph) -> bool: """ Checks if the graphs of two MoleculeGraphs are isomorphic to one another. In order to prevent problems with misdirected edges, both graphs are converted into undirected nx.Graph objects. - :param other: MoleculeGraph object to be compared. + Args: + other: MoleculeGraph object to be compared. Returns: bool @@ -2755,11 +2793,11 @@ def diff(self, other, strict=True): same if the underlying Molecules are ordered differently. - :param other: MoleculeGraph - :param strict: if False, will compare bonds - from different Molecules, with node indices - replaced by Species strings, will not count - number of occurrences of bonds + Args: + other: MoleculeGraph + strict: if False, will compare bonds + from different Molecules, with node indices replaced by Species + strings, will not count number of occurrences of bonds """ if self.molecule != other.molecule and strict: return ValueError("Meaningless to compare MoleculeGraphs if corresponding Molecules are different.") diff --git a/pymatgen/analysis/local_env.py b/pymatgen/analysis/local_env.py index 6ca1a1a1f92..911ee16e040 100644 --- a/pymatgen/analysis/local_env.py +++ b/pymatgen/analysis/local_env.py @@ -407,7 +407,7 @@ def get_all_nn_info(self, structure: Structure): Args: structure (Structure): Input structure - Return: + Returns: List of NN site information for each site in the structure. Each entry has the same format as `get_nn_info` """ @@ -1612,8 +1612,9 @@ def get_nn_info(self, structure: Structure, n: int): Get all near-neighbor sites and weights (orders) of bonds for a given atom. - :param structure: input Molecule. - :param n: index of site for which to determine near neighbors. + Args: + structure: input Molecule. + n: index of site for which to determine near neighbors. Returns: [dict] representing a neighboring site and the type of diff --git a/pymatgen/analysis/molecule_matcher.py b/pymatgen/analysis/molecule_matcher.py index e4056d31536..b069f90bb7b 100644 --- a/pymatgen/analysis/molecule_matcher.py +++ b/pymatgen/analysis/molecule_matcher.py @@ -299,7 +299,7 @@ def _virtual_molecule(self, mol, ilabels, eq_atoms): farthest_group_idx: The equivalent atom group index in which there is the farthest atom to the centroid - Return: + Returns: The virtual molecule """ vmol = openbabel.OBMol() @@ -350,7 +350,7 @@ def _align_heavy_atoms(mol1, mol2, vmol1, vmol2, ilabel1, ilabel2, eq_atoms): ilabel2: inchi label map of the second molecule eq_atoms: equivalent atom labels - Return: + Returns: corrected inchi labels of heavy atoms of the second molecule """ n_virtual = vmol1.NumAtoms() @@ -423,7 +423,7 @@ def _align_hydrogen_atoms(mol1, mol2, heavy_indices1, heavy_indices2): heavy_indices1: inchi label map of the first molecule heavy_indices2: label map of the second molecule - Return: + Returns: corrected label map of all atoms of the second molecule """ num_atoms = mol2.NumAtoms() diff --git a/pymatgen/analysis/nmr.py b/pymatgen/analysis/nmr.py index b5a0c8cb31a..7dc15301146 100644 --- a/pymatgen/analysis/nmr.py +++ b/pymatgen/analysis/nmr.py @@ -202,7 +202,7 @@ def coupling_constant(self, specie): Can take a isotope or element string, Species object, or Site object - Return: + Returns: the coupling constant as a FloatWithUnit in MHz """ planks_constant = FloatWithUnit(6.62607004e-34, "m^2 kg s^-1") diff --git a/pymatgen/analysis/piezo_sensitivity.py b/pymatgen/analysis/piezo_sensitivity.py index ad2fce63e09..c53c2bce8c2 100644 --- a/pymatgen/analysis/piezo_sensitivity.py +++ b/pymatgen/analysis/piezo_sensitivity.py @@ -64,7 +64,7 @@ def get_BEC_operations(self, eigtol=1e-5, opstol=1e-3): opstol (float): tolerance for determining if a symmetry operation relates two sites - Return: + Returns: list of symmetry operations mapping equivalent sites and the indexes of those sites. """ @@ -117,7 +117,7 @@ def get_rand_BEC(self, max_charge=1): Args: max_charge (float): maximum born effective charge value - Return: + Returns: np.array Born effective charge tensor """ n_atoms = len(self.structure) @@ -197,7 +197,7 @@ def get_IST_operations(self, opstol=1e-3): opstol (float): tolerance for determining if a symmetry operation relates two sites - Return: + Returns: list of symmetry operations mapping equivalent sites and the indexes of those sites. """ @@ -231,7 +231,7 @@ def get_rand_IST(self, max_force=1): Args: max_force (float): maximum born effective charge value - Return: + Returns: InternalStrainTensor """ n_atoms = len(self.structure) @@ -289,7 +289,7 @@ def get_FCM_operations(self, eigtol=1e-5, opstol=1e-5): opstol (float): tolerance for determining if a symmetry operation relates two sites - Return: + Returns: list of symmetry operations mapping equivalent sites and the indexes of those sites. """ @@ -357,7 +357,7 @@ def get_unstable_FCM(self, max_force=1): Args: max_charge (float): maximum born effective charge value - Return: + Returns: numpy array representing the force constant matrix """ struct = self.structure @@ -417,7 +417,7 @@ def get_symmetrized_FCM(self, unsymmetrized_fcm, max_force=1): unsymmetrized_fcm (numpy array): unsymmetrized force constant matrix max_charge (float): maximum born effective charge value - Return: + Returns: 3Nx3N numpy array representing the force constant matrix """ operations = self.FCM_operations @@ -478,7 +478,7 @@ def get_stable_FCM(self, fcm, fcmasum=10): fcmasum (int): number of iterations to attempt to obey the acoustic sum rule - Return: + Returns: 3Nx3N numpy array representing the force constant matrix """ check = 0 @@ -526,7 +526,7 @@ def get_asum_FCM(self, fcm: np.ndarray, numiter: int = 15): numiter (int): number of iterations to attempt to obey the acoustic sum rule - Return: + Returns: numpy array representing the force constant matrix """ # set max force in reciprocal space @@ -617,7 +617,7 @@ def get_rand_FCM(self, asum=15, force=10): asum (int): number of iterations to attempt to obey the acoustic sum rule - Return: + Returns: NxNx3x3 np.array representing the force constant matrix """ from pymatgen.io.phonopy import get_phonopy_structure @@ -664,7 +664,7 @@ def get_piezo(BEC, IST, FCM, rcond=0.0001): FCM (numpy array): NxNx3x3 array representing the born effective charge tensor rcondy (float): condition for excluding eigenvalues in the pseudoinverse - Return: + Returns: 3x3x3 calculated Piezo tensor """ n_sites = len(BEC) @@ -694,7 +694,7 @@ def rand_piezo(struct, pointops, sharedops, BEC, IST, FCM, anumiter=10): IST (numpy array): Nx3x3x3 array representing the internal strain tensor FCM (numpy array): NxNx3x3 array representing the born effective charge tensor anumiter (int): number of iterations for acoustic sum rule convergence - Return: + Returns: list in the form of [Nx3x3 random born effective charge tenosr, Nx3x3x3 random internal strain tensor, NxNx3x3 random force constant matrix, 3x3x3 piezo tensor] """ diff --git a/pymatgen/analysis/quasirrho.py b/pymatgen/analysis/quasirrho.py index 8f62d8f8104..4de59a992ed 100644 --- a/pymatgen/analysis/quasirrho.py +++ b/pymatgen/analysis/quasirrho.py @@ -135,7 +135,6 @@ def __init__( @classmethod def from_gaussian_output(cls, output: GaussianOutput, **kwargs) -> QuasiRRHO: """ - Args: output (GaussianOutput): Pymatgen GaussianOutput object @@ -151,7 +150,6 @@ def from_gaussian_output(cls, output: GaussianOutput, **kwargs) -> QuasiRRHO: @classmethod def from_qc_output(cls, output: QCOutput, **kwargs) -> QuasiRRHO: """ - Args: output (QCOutput): Pymatgen QCOutput object diff --git a/pymatgen/analysis/structure_matcher.py b/pymatgen/analysis/structure_matcher.py index 772e41603bd..5e862f4568f 100644 --- a/pymatgen/analysis/structure_matcher.py +++ b/pymatgen/analysis/structure_matcher.py @@ -77,7 +77,8 @@ def get_hash(self, composition): @classmethod def from_dict(cls, d): """ - :param d: Dict representation + Args: + d: Dict representation Returns: Comparator. @@ -267,7 +268,10 @@ def are_equal(self, sp1, sp2) -> bool: def get_hash(self, composition): """ - :param composition: Composition. + Args: + composition: Composition. + + TODO: might need a proper hash method Returns: 1. Difficult to define sensible hash @@ -856,7 +860,8 @@ def as_dict(self): @classmethod def from_dict(cls, d): """ - :param d: Dict representation + Args: + d: Dict representation Returns: StructureMatcher diff --git a/pymatgen/analysis/structure_prediction/substitutor.py b/pymatgen/analysis/structure_prediction/substitutor.py index e4ac2565262..354f4ee5b14 100644 --- a/pymatgen/analysis/structure_prediction/substitutor.py +++ b/pymatgen/analysis/structure_prediction/substitutor.py @@ -174,7 +174,7 @@ def pred_from_list(self, species_list): There are an exceptionally large number of substitutions to look at (260^n), where n is the number of species in the list. We need a more efficient than brute force way of going - through these possibilities. The brute force method would be:: + through these possibilities. The brute force method would be: output = [] for p in itertools.product(self._sp.species_list, repeat=len(species_list)): diff --git a/pymatgen/analysis/surface_analysis.py b/pymatgen/analysis/surface_analysis.py index 7dc94233b65..9133d3f12a3 100644 --- a/pymatgen/analysis/surface_analysis.py +++ b/pymatgen/analysis/surface_analysis.py @@ -1,7 +1,7 @@ """ This module defines tools to analyze surface and adsorption related quantities as well as related plots. If you use this module, please -consider citing the following works:: +consider citing the following works: R. Tran, Z. Xu, B. Radhakrishnan, D. Winston, W. Sun, K. A. Persson, S. P. Ong, "Surface Energies of Elemental Crystals", Scientific @@ -20,7 +20,7 @@ Computational Materials, 3(1), 14. https://doi.org/10.1038/s41524-017-0017-z -Todo: +TODO: - Still assumes individual elements have their own chempots in a molecular adsorbate instead of considering a single chempot for a single molecular adsorbate. E.g. for an OH diff --git a/pymatgen/analysis/transition_state.py b/pymatgen/analysis/transition_state.py index 542aadf0d49..b9dc6675629 100644 --- a/pymatgen/analysis/transition_state.py +++ b/pymatgen/analysis/transition_state.py @@ -296,7 +296,9 @@ def combine_neb_plots(neb_analyses, arranged_neb_analyses=False, reverse_plot=Fa Note that the barrier labeled in y-axis in the combined plot might be different from that in the individual plot due to the reference energy used. reverse_plot: reverse the plot or percolation direction. - return: a NEBAnalysis object + + Returns: + a NEBAnalysis object """ x = StructureMatcher() for neb_index, neb in enumerate(neb_analyses): diff --git a/pymatgen/analysis/wulff.py b/pymatgen/analysis/wulff.py index c7d30805cab..655b9b10a11 100644 --- a/pymatgen/analysis/wulff.py +++ b/pymatgen/analysis/wulff.py @@ -74,13 +74,14 @@ class WulffFacet: def __init__(self, normal, e_surf, normal_pt, dual_pt, index, m_ind_orig, miller): """ - :param normal: - :param e_surf: - :param normal_pt: - :param dual_pt: - :param index: - :param m_ind_orig: - :param miller: + Args: + normal: + e_surf: + normal_pt: + dual_pt: + index: + m_ind_orig: + miller: """ self.normal = normal self.e_surf = e_surf @@ -376,7 +377,7 @@ def get_plot( units_in_JPERM2 (bool): Units of surface energy, defaults to Joules per square meter (True) - Return: + Returns: (matplotlib.pyplot) """ from mpl_toolkits.mplot3d import art3d @@ -496,7 +497,7 @@ def get_plotly( units_in_JPERM2 (bool): Units of surface energy, defaults to Joules per square meter (True) - Return: + Returns: (plotly.graph_objects.Figure) """ units = "Jm⁻²" if units_in_JPERM2 else "eVÅ⁻²" diff --git a/pymatgen/analysis/xps.py b/pymatgen/analysis/xps.py index 64606c1ca61..1df46ad9b21 100644 --- a/pymatgen/analysis/xps.py +++ b/pymatgen/analysis/xps.py @@ -1,14 +1,14 @@ """ This is a module for XPS analysis. It is modelled after the Galore package (https://github.com/SMTG-UCL/galore), but with some modifications for easier analysis from pymatgen itself. Please cite the following original work if you use -this:: +this: Adam J. Jackson, Alex M. Ganose, Anna Regoutz, Russell G. Egdell, David O. Scanlon (2018). Galore: Broadening and weighting for simulation of photoelectron spectroscopy. Journal of Open Source Software, 3(26), 773, doi: 10.21105/joss.007733 You may wish to look at the optional dependency galore for more functionality such as plotting and other cross-sections. -Note that the atomic_subshell_photoionization_cross_sections.csv has been reparsed from the original compilation:: +Note that the atomic_subshell_photoionization_cross_sections.csv has been reparsed from the original compilation: Yeh, J. J.; Lindau, I. Atomic Subshell Photoionization Cross Sections and Asymmetry Parameters: 1 ⩽ Z ⩽ 103. Atomic Data and Nuclear Data Tables 1985, 32 (1), 1-155. https://doi.org/10.1016/0092-640X(85)90016-6. @@ -78,8 +78,10 @@ class XPS(Spectrum): @classmethod def from_dos(cls, dos: CompleteDos): """ - :param dos: CompleteDos object with project element-orbital DOS. Can be obtained from Vasprun.get_complete_dos. - :param sigma: Smearing for Gaussian. + Args: + dos: CompleteDos object with project element-orbital DOS. + Can be obtained from Vasprun.get_complete_dos. + sigma: Smearing for Gaussian. Returns: XPS diff --git a/pymatgen/cli/pmg.py b/pymatgen/cli/pmg.py index 2524a9cead4..ba8d3353931 100755 --- a/pymatgen/cli/pmg.py +++ b/pymatgen/cli/pmg.py @@ -22,7 +22,8 @@ def parse_view(args): """Handle view commands. - :param args: Args from command. + Args: + args: Args from command. """ from pymatgen.vis.structure_vtk import StructureVis @@ -37,7 +38,8 @@ def parse_view(args): def diff_incar(args): """Handle diff commands. - :param args: Args from command. + Args: + args: Args from command. """ filepath1 = args.incars[0] filepath2 = args.incars[1] diff --git a/pymatgen/cli/pmg_config.py b/pymatgen/cli/pmg_config.py index a1cc86cb758..3ec919857af 100755 --- a/pymatgen/cli/pmg_config.py +++ b/pymatgen/cli/pmg_config.py @@ -170,7 +170,8 @@ def setup_potcars(potcar_dirs: list[str]): def build_enum(fortran_command: str = "gfortran") -> bool: """Build enum. - :param fortran_command: + Args: + fortran_command: The Fortran compiler command. """ cwd = os.getcwd() state = True @@ -196,7 +197,8 @@ def build_enum(fortran_command: str = "gfortran") -> bool: def build_bader(fortran_command="gfortran"): """Build bader package. - :param fortran_command: + Args: + fortran_command: The Fortran compiler command. """ bader_url = "http://theory.cm.utexas.edu/henkelman/code/bader/download/bader.tar.gz" cwd = os.getcwd() diff --git a/pymatgen/command_line/bader_caller.py b/pymatgen/command_line/bader_caller.py index 14590e6c79b..b0b33d60ae8 100644 --- a/pymatgen/command_line/bader_caller.py +++ b/pymatgen/command_line/bader_caller.py @@ -508,8 +508,10 @@ def bader_analysis_from_path(path: str, suffix: str = ""): 3. Runs Bader analysis twice: once for charge, and a second time for the charge difference (magnetization density). - :param path: path to folder to search in - :param suffix: specific suffix to look for (e.g. '.relax1' for 'CHGCAR.relax1.gz' + Args: + path: path to folder to search in + suffix: specific suffix to look for (e.g. '.relax1' for 'CHGCAR.relax1.gz' + Returns: summary dict """ @@ -566,10 +568,11 @@ def bader_analysis_from_objects( 2. Runs Bader analysis twice: once for charge, and a second time for the charge difference (magnetization density). - :param chgcar: Chgcar object - :param potcar: (optional) Potcar object - :param aeccar0: (optional) Chgcar object from aeccar0 file - :param aeccar2: (optional) Chgcar object from aeccar2 file + Args: + chgcar: Chgcar object + potcar: (optional) Potcar object + aeccar0: (optional) Chgcar object from aeccar0 file + aeccar2: (optional) Chgcar object from aeccar2 file Returns: summary dict diff --git a/pymatgen/command_line/chargemol_caller.py b/pymatgen/command_line/chargemol_caller.py index 75c8eba084a..7f2fc1bcd91 100644 --- a/pymatgen/command_line/chargemol_caller.py +++ b/pymatgen/command_line/chargemol_caller.py @@ -59,6 +59,8 @@ if TYPE_CHECKING: from pathlib import Path + from pymatgen.core import Structure + __author__ = "Martin Siron, Andrew S. Rosen" __version__ = "0.1" __maintainer__ = "Shyue Ping Ong" @@ -109,21 +111,26 @@ def __init__( self._potcar_path = self._get_filepath(path, "POTCAR") self._aeccar0_path = self._get_filepath(path, "AECCAR0") self._aeccar2_path = self._get_filepath(path, "AECCAR2") + if run_chargemol and not ( self._chgcar_path and self._potcar_path and self._aeccar0_path and self._aeccar2_path ): raise FileNotFoundError("CHGCAR, AECCAR0, AECCAR2, and POTCAR are all needed for Chargemol.") + if self._chgcar_path: - self.chgcar = Chgcar.from_file(self._chgcar_path) - self.structure = self.chgcar.structure - self.natoms = self.chgcar.poscar.natoms + self.chgcar: Chgcar | None = Chgcar.from_file(self._chgcar_path) + self.structure: Structure | None = self.chgcar.structure + self.natoms: list[int] | None = self.chgcar.poscar.natoms + else: self.chgcar = self.structure = self.natoms = None warnings.warn("No CHGCAR found. Some properties may be unavailable.", UserWarning) + if self._potcar_path: self.potcar = Potcar.from_file(self._potcar_path) else: warnings.warn("No POTCAR found. Some properties may be unavailable.", UserWarning) + self.aeccar0 = Chgcar.from_file(self._aeccar0_path) if self._aeccar0_path else None self.aeccar2 = Chgcar.from_file(self._aeccar2_path) if self._aeccar2_path else None diff --git a/pymatgen/command_line/critic2_caller.py b/pymatgen/command_line/critic2_caller.py index 5a8a6215c58..372d0e9ebb2 100644 --- a/pymatgen/command_line/critic2_caller.py +++ b/pymatgen/command_line/critic2_caller.py @@ -61,6 +61,8 @@ from pymatgen.util.due import Doi, due if TYPE_CHECKING: + from typing_extensions import Self + from pymatgen.core import Structure logging.basicConfig(level=logging.INFO) @@ -84,25 +86,26 @@ class Critic2Caller: "Critic2Caller requires the executable critic to be in the path. " "Please follow the instructions at https://github.com/aoterodelaroza/critic2.", ) - def __init__(self, input_script): + def __init__(self, input_script: str): """Run Critic2 on a given input script. - :param input_script: string defining the critic2 input + Args: + input_script: string defining the critic2 input """ # store if examining the input script is useful, # not otherwise used self._input_script = input_script - with open("input_script.cri", mode="w") as file: + with open("input_script.cri", mode="w", encoding="utf-8") as file: file.write(input_script) args = ["critic2", "input_script.cri"] with subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE, close_fds=True) as rs: - stdout, stderr = rs.communicate() - stdout = stdout.decode() + _stdout, _stderr = rs.communicate() + stdout = _stdout.decode() - if stderr: - stderr = stderr.decode() + if _stderr: + stderr = _stderr.decode() warnings.warn(stderr) if rs.returncode != 0: @@ -127,7 +130,7 @@ def from_chgcar( write_cml=False, write_json=True, zpsp=None, - ): + ) -> Self: """Run Critic2 in automatic mode on a supplied structure, charge density (chgcar) and reference charge density (chgcar_ref). @@ -165,20 +168,21 @@ def from_chgcar( sub-dividing the Wigner-Seitz cell and between every atom pair closer than 10 Bohr, see critic2 manual for more options - :param structure: Structure to analyze - :param chgcar: Charge density to use for analysis. If None, will - use promolecular density. Should be a Chgcar object or path (string). - :param chgcar_ref: Reference charge density. If None, will use - chgcar as reference. Should be a Chgcar object or path (string). - :param user_input_settings (dict): as explained above - :param write_cml (bool): Useful for debug, if True will write all - critical points to a file 'table.cml' in the working directory - useful for visualization - :param write_json (bool): Whether to write out critical points - and YT json. YT integration will be performed with this setting. - :param zpsp (dict): Dict of element/symbol name to number of electrons - (ZVAL in VASP pseudopotential), with which to properly augment core regions - and calculate charge transfer. Optional. + Args: + structure: Structure to analyze + chgcar: Charge density to use for analysis. If None, will + use promolecular density. Should be a Chgcar object or path (string). + chgcar_ref: Reference charge density. If None, will use + chgcar as reference. Should be a Chgcar object or path (string). + user_input_settings (dict): as explained above + write_cml (bool): Useful for debug, if True will write all + critical points to a file 'table.cml' in the working directory + useful for visualization + write_json (bool): Whether to write out critical points + and YT json. YT integration will be performed with this setting. + zpsp (dict): Dict of element/symbol name to number of electrons + (ZVAL in VASP pseudopotential), with which to properly augment core regions + and calculate charge transfer. Optional. """ settings = {"CPEPS": 0.1, "SEED": ["WS", "PAIR DIST 10"]} if user_input_settings: @@ -219,7 +223,7 @@ def from_chgcar( input_script += ["yt"] input_script += ["yt JSON yt.json"] - input_script = "\n".join(input_script) + input_script_str = "\n".join(input_script) with ScratchDir("."): structure.to(filename="POSCAR") @@ -234,7 +238,7 @@ def from_chgcar( elif chgcar_ref: os.symlink(chgcar_ref, "ref.CHGCAR") - caller = cls(input_script) + caller = cls(input_script_str) caller.output = Critic2Analysis( structure, @@ -248,7 +252,7 @@ def from_chgcar( return caller @classmethod - def from_path(cls, path, suffix="", zpsp=None): + def from_path(cls, path, suffix="", zpsp=None) -> Self: """Convenience method to run critic2 analysis on a folder with typical VASP output files. This method will: @@ -262,10 +266,11 @@ def from_path(cls, path, suffix="", zpsp=None): 3. Runs critic2 analysis twice: once for charge, and a second time for the charge difference (magnetization density). - :param path: path to folder to search in - :param suffix: specific suffix to look for (e.g. '.relax1' for - 'CHGCAR.relax1.gz') - :param zpsp: manually specify ZPSP if POTCAR not present + Args: + path: path to folder to search in + suffix: specific suffix to look for (e.g. '.relax1' for + 'CHGCAR.relax1.gz') + zpsp: manually specify ZPSP if POTCAR not present """ chgcar_path = get_filepath("CHGCAR", "Could not find CHGCAR!", path, suffix) chgcar = Chgcar.from_file(chgcar_path) @@ -359,15 +364,16 @@ def __init__( Note this class is usually associated with a Structure, so has information on multiplicity/point group symmetry. - :param index: index of point - :param type: type of point, given as a string - :param coords: Cartesian coordinates in Angstroms - :param frac_coords: fractional coordinates - :param point_group: point group associated with critical point - :param multiplicity: number of equivalent critical points - :param field: value of field at point (f) - :param field_gradient: gradient of field at point (grad f) - :param field_hessian: hessian of field at point (del^2 f) + Args: + index: index of point + type: type of point, given as a string + coords: Cartesian coordinates in Angstroms + frac_coords: fractional coordinates + point_group: point group associated with critical point + multiplicity: number of equivalent critical points + field: value of field at point (f) + field_gradient: gradient of field at point (grad f) + field_hessian: hessian of field at point (del^2 f) """ self.index = index self._type = type @@ -441,16 +447,15 @@ class with bonding information. By default, this returns Only one of (stdout, cpreport) required, with cpreport preferred since this is a new, native JSON output from critic2. - :param structure: associated Structure - :param stdout: stdout from running critic2 in automatic - mode - :param stderr: stderr from running critic2 in automatic - mode - :param cpreport: json output from CPREPORT command - :param yt: json output from YT command - :param zpsp (dict): Dict of element/symbol name to number of electrons - (ZVAL in VASP pseudopotential), with which to calculate charge transfer. - Optional. + Args: + structure: associated Structure + stdout: stdout from running critic2 in automatic mode + stderr: stderr from running critic2 in automatic mode + cpreport: json output from CPREPORT command + yt: json output from YT command + zpsp (dict): Dict of element/symbol name to number of electrons + (ZVAL in VASP pseudopotential), with which to calculate charge transfer. + Optional. Args: structure (Structure): Associated Structure. @@ -867,10 +872,11 @@ def _parse_stdout(self, stdout): def _add_node(self, idx, unique_idx, frac_coords): """Add information about a node describing a critical point. - :param idx: index - :param unique_idx: index of unique CriticalPoint, - used to look up more information of point (field etc.) - :param frac_coord: fractional coordinates of point + Args: + idx: index + unique_idx: index of unique CriticalPoint, + used to look up more information of point (field etc.) + frac_coords: fractional coordinates of point """ self.nodes[idx] = {"unique_idx": unique_idx, "frac_coords": frac_coords} @@ -887,13 +893,14 @@ def _add_edge(self, idx, from_idx, from_lvec, to_idx, to_lvec): this as a single edge linking nuclei with the properties of the bond critical point stored as an edge attribute. - :param idx: index of node - :param from_idx: from index of node - :param from_lvec: vector of lattice image the from node is in - as tuple of ints - :param to_idx: to index of node - :param to_lvec: vector of lattice image the to node is in as - tuple of ints + Args: + idx: index of node + from_idx: from index of node + from_lvec: vector of lattice image the from node is in + as tuple of ints + to_idx: to index of node + to_lvec: vector of lattice image the to node is in as + tuple of ints """ self.edges[idx] = { "from_idx": from_idx, diff --git a/pymatgen/core/bonds.py b/pymatgen/core/bonds.py index 9b4fdfce8ef..1c45b74f931 100644 --- a/pymatgen/core/bonds.py +++ b/pymatgen/core/bonds.py @@ -117,7 +117,7 @@ def obtain_all_bond_lengths(sp1, sp2, default_bl: float | None = None): bond length as a default value (bond order = 1). If None, a ValueError will be thrown. - Return: + Returns: A dict mapping bond order to bond length in angstrom """ if isinstance(sp1, Element): diff --git a/pymatgen/core/interface.py b/pymatgen/core/interface.py index cab443c1070..0b46dc1c976 100644 --- a/pymatgen/core/interface.py +++ b/pymatgen/core/interface.py @@ -283,7 +283,9 @@ def as_dict(self): @classmethod def from_dict(cls, dct: dict) -> Interface: # type: ignore[override] - """:param dct: dict + """ + Args: + dct: dict. Returns: Creates slab from dict. diff --git a/pymatgen/core/ion.py b/pymatgen/core/ion.py index 3a1d29e6c57..36d94437509 100644 --- a/pymatgen/core/ion.py +++ b/pymatgen/core/ion.py @@ -34,7 +34,8 @@ def from_formula(cls, formula: str) -> Ion: Also note that (aq) can be included in the formula, e.g. "NaOH (aq)". - :param formula: + Args: + formula (str): The formula to create ion from. Returns: Ion diff --git a/pymatgen/core/lattice.py b/pymatgen/core/lattice.py index 70c327efe4b..4f88f306b85 100644 --- a/pymatgen/core/lattice.py +++ b/pymatgen/core/lattice.py @@ -958,7 +958,9 @@ def find_mapping( return next(self.find_all_mappings(other_lattice, ltol, atol, skip_rotation_matrix), None) def get_lll_reduced_lattice(self, delta: float = 0.75) -> Lattice: - """:param delta: Delta parameter. + """ + Args: + delta: Delta parameter. Returns: LLL reduced Lattice. @@ -1509,8 +1511,10 @@ def get_all_distances( return np.sqrt(d2) def is_hexagonal(self, hex_angle_tol: float = 5, hex_length_tol: float = 0.01) -> bool: - """:param hex_angle_tol: Angle tolerance - :param hex_length_tol: Length tolerance + """ + Args: + hex_angle_tol: Angle tolerance + hex_length_tol: Length tolerance. Returns: Whether lattice corresponds to hexagonal lattice. diff --git a/pymatgen/core/operations.py b/pymatgen/core/operations.py index f1b9d26d988..cb6d5652a5f 100644 --- a/pymatgen/core/operations.py +++ b/pymatgen/core/operations.py @@ -394,7 +394,7 @@ def rotoreflection(axis: ArrayLike, angle: float, origin: ArrayLike = (0, 0, 0)) origin (3x1 array): Point left invariant by roto-reflection. Defaults to (0, 0, 0). - Return: + Returns: Roto-reflection operation """ rot = SymmOp.from_origin_axis_angle(origin, axis, angle) @@ -452,7 +452,9 @@ def from_xyz_str(cls, xyz_str: str) -> SymmOp: @classmethod def from_dict(cls, dct) -> SymmOp: - """:param dct: dict + """ + Args: + dct: dict. Returns: SymmOp from dict representation. @@ -610,7 +612,9 @@ def as_dict(self) -> dict[str, Any]: @classmethod def from_dict(cls, dct: dict) -> MagSymmOp: - """:param dct: dict + """ + Args: + dct: dict. Returns: MagneticSymmOp from dict representation. diff --git a/pymatgen/core/periodic_table.py b/pymatgen/core/periodic_table.py index 3d004350767..74fcf9463fb 100644 --- a/pymatgen/core/periodic_table.py +++ b/pymatgen/core/periodic_table.py @@ -1227,7 +1227,9 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d) -> Species: - """:param d: Dict representation. + """ + Args: + d: Dict representation. Returns: Species. @@ -1384,7 +1386,9 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d) -> DummySpecies: - """:param d: Dict representation + """ + Args: + d: Dict representation. Returns: DummySpecies diff --git a/pymatgen/core/sites.py b/pymatgen/core/sites.py index 2132238ed83..1506197da15 100644 --- a/pymatgen/core/sites.py +++ b/pymatgen/core/sites.py @@ -40,21 +40,22 @@ def __init__( ) -> None: """Creates a non-periodic Site. - :param species: Species on the site. Can be: - i. A Composition-type object (preferred) - ii. An element / species specified either as a string - symbols, e.g. "Li", "Fe2+", "P" or atomic numbers, - e.g., 3, 56, or actual Element or Species objects. - iii.Dict of elements/species and occupancies, e.g., - {"Fe" : 0.5, "Mn":0.5}. This allows the setup of - disordered structures. - :param coords: Cartesian coordinates of site. - :param properties: Properties associated with the site as a dict, e.g. - {"magmom": 5}. Defaults to None. - :param label: Label for the site. Defaults to None. - :param skip_checks: Whether to ignore all the usual checks and just - create the site. Use this if the Site is created in a controlled - manner and speed is desired. + Args: + species: Species on the site. Can be: + i. A Composition-type object (preferred) + ii. An element / species specified either as a string + symbols, e.g. "Li", "Fe2+", "P" or atomic numbers, + e.g., 3, 56, or actual Element or Species objects. + iii.Dict of elements/species and occupancies, e.g., + {"Fe" : 0.5, "Mn":0.5}. This allows the setup of + disordered structures. + coords: Cartesian coordinates of site. + properties: Properties associated with the site as a dict, e.g. + {"magmom": 5}. Defaults to None. + label: Label for the site. Defaults to None. + skip_checks: Whether to ignore all the usual checks and just + create the site. Use this if the Site is created in a controlled + manner and speed is desired. """ if not skip_checks: if not isinstance(species, Composition): @@ -298,28 +299,29 @@ def __init__( ) -> None: """Create a periodic site. - :param species: Species on the site. Can be: - i. A Composition-type object (preferred) - ii. An element / species specified either as a string - symbols, e.g. "Li", "Fe2+", "P" or atomic numbers, - e.g., 3, 56, or actual Element or Species objects. - iii.Dict of elements/species and occupancies, e.g., - {"Fe" : 0.5, "Mn":0.5}. This allows the setup of - disordered structures. - :param coords: Coordinates of site, fractional coordinates - by default. See ``coords_are_cartesian`` for more details. - :param lattice: Lattice associated with the site. - :param to_unit_cell: Translates fractional coordinate to the - basic unit cell, i.e. all fractional coordinates satisfy 0 - <= a < 1. Defaults to False. - :param coords_are_cartesian: Set to True if you are providing - Cartesian coordinates. Defaults to False. - :param properties: Properties associated with the site as a dict, e.g. - {"magmom": 5}. Defaults to None. - :param label: Label for the site. Defaults to None. - :param skip_checks: Whether to ignore all the usual checks and just - create the site. Use this if the PeriodicSite is created in a - controlled manner and speed is desired. + Args: + species: Species on the site. Can be: + i. A Composition-type object (preferred) + ii. An element / species specified either as a string + symbols, e.g. "Li", "Fe2+", "P" or atomic numbers, + e.g., 3, 56, or actual Element or Species objects. + iii.Dict of elements/species and occupancies, e.g., + {"Fe" : 0.5, "Mn":0.5}. This allows the setup of + disordered structures. + coords: Coordinates of site, fractional coordinates + by default. See ``coords_are_cartesian`` for more details. + lattice: Lattice associated with the site. + to_unit_cell: Translates fractional coordinate to the + basic unit cell, i.e. all fractional coordinates satisfy 0 + <= a < 1. Defaults to False. + coords_are_cartesian: Set to True if you are providing + Cartesian coordinates. Defaults to False. + properties: Properties associated with the site as a dict, e.g. + {"magmom": 5}. Defaults to None. + label: Label for the site. Defaults to None. + skip_checks: Whether to ignore all the usual checks and just + create the site. Use this if the PeriodicSite is created in a + controlled manner and speed is desired. """ frac_coords = lattice.get_fractional_coords(coords) if coords_are_cartesian else coords diff --git a/pymatgen/core/spectrum.py b/pymatgen/core/spectrum.py index b50ad021864..7e819edc397 100644 --- a/pymatgen/core/spectrum.py +++ b/pymatgen/core/spectrum.py @@ -18,9 +18,12 @@ def lorentzian(x, x_0: float = 0, sigma: float = 1.0): - """:param x: x values - :param x_0: Center - :param sigma: FWHM + """ + + Args: + x: x values + x_0: Center + sigma: FWHM. Returns: Value of lorentzian at x. diff --git a/pymatgen/core/structure.py b/pymatgen/core/structure.py index 4374dda8266..6760dc90a81 100644 --- a/pymatgen/core/structure.py +++ b/pymatgen/core/structure.py @@ -80,12 +80,14 @@ def __init__( index: int = 0, label: str | None = None, ) -> None: - """:param species: Same as Site - :param coords: Same as Site, but must be fractional. - :param properties: Same as Site - :param nn_distance: Distance to some other Site. - :param index: Index within structure. - :param label: Label for the site. Defaults to None. + """ + Args: + species: Same as Site + coords: Same as Site, but must be fractional. + properties: Same as Site + nn_distance: Distance to some other Site. + index: Index within structure. + label: Label for the site. Defaults to None. """ self.coords = coords self._species = species diff --git a/pymatgen/core/surface.py b/pymatgen/core/surface.py index 087f1e26651..05873178c2f 100644 --- a/pymatgen/core/surface.py +++ b/pymatgen/core/surface.py @@ -482,7 +482,9 @@ def as_dict(self): @classmethod def from_dict(cls, dct: dict) -> Slab: # type: ignore[override] - """:param dct: dict + """ + Args: + dct: dict. Returns: Creates slab from dict. @@ -586,8 +588,9 @@ def get_symmetric_site(self, point, cartesian=False): symmetric properties of a slab when creating adsorbed structures or symmetric reconstructions. - Arg: + Args: point: Fractional coordinate. + cartesian: Where to use Cartesian coordinate. Returns: point: Fractional coordinate. A point equivalent to the @@ -623,7 +626,7 @@ def symmetrically_add_atom(self, specie, point, coords_are_cartesian=False) -> N Will add the corresponding site on the other side of the slab to maintain equivalent surfaces. - Arg: + Args: specie (str): The specie to add point (coords): The coordinate of the site in the slab to add. coords_are_cartesian (bool): Is the point in Cartesian coordinates @@ -645,7 +648,7 @@ def symmetrically_remove_atoms(self, indices) -> None: Will remove the corresponding site on the other side of the slab to maintain equivalent surfaces. - Arg: + Args: indices ([indices]): The indices of the sites in the slab to remove. """ @@ -875,7 +878,7 @@ def get_slab(self, shift=0, tol: float = 0.1, energy=None): method. Instead, it is used by other generation algorithms to obtain all slabs. - Arg: + Args: shift (float): A shift value in Angstrom that determines how much a slab should be shifted. tol (float): Tolerance to determine primitive cell. @@ -1111,7 +1114,7 @@ def repair_broken_bonds(self, slab, bonds): In a future release of surface.py, the ghost_sites will be used to tell us how the repair bonds should look like. - Arg: + Args: slab (structure): A structure object representing a slab. bonds ({(specie1, specie2): max_bond_dist}: bonds are specified as a dict of tuples: float of specie1, specie2 @@ -1174,7 +1177,7 @@ def move_to_other_side(self, init_slab, index_of_sites): """This method will Move a set of sites to the other side of the slab (opposite surface). - Arg: + Args: init_slab (structure): A structure object representing a slab. index_of_sites (list of ints): The list of indices representing the sites we want to move to the other side. @@ -1225,7 +1228,7 @@ def nonstoichiometric_symmetrized_slab(self, init_slab): can destroy the stoichiometry of the slab. For non-elemental structures, the chemical potential will be needed to calculate surface energy. - Arg: + Args: init_slab (Structure): A single slab structure Returns: diff --git a/pymatgen/core/tensors.py b/pymatgen/core/tensors.py index 9aa04d755d6..236b380cf1c 100644 --- a/pymatgen/core/tensors.py +++ b/pymatgen/core/tensors.py @@ -25,6 +25,8 @@ if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Self + from pymatgen.core import Structure __author__ = "Joseph Montoya" @@ -361,7 +363,7 @@ def get_voigt_dict(rank): return vdict @classmethod - def from_voigt(cls, voigt_input): + def from_voigt(cls, voigt_input) -> Self: """Constructor based on the voigt notation vector or matrix. Args: @@ -510,7 +512,7 @@ def from_values_indices( voigt_rank=None, vsym=True, verbose=False, - ): + ) -> Self: """Creates a tensor from values and indices, with options for populating the remainder of the tensor. @@ -533,18 +535,21 @@ def from_values_indices( # TODO: refactor rank inheritance to make this easier indices = np.array(indices) if voigt_rank: - shape = [3] * (voigt_rank % 2) + [6] * (voigt_rank // 2) + shape = np.array([3] * (voigt_rank % 2) + [6] * (voigt_rank // 2)) else: shape = np.ceil(np.max(indices + 1, axis=0) / 3.0) * 3 + base = np.zeros(shape.astype(int)) for v, idx in zip(values, indices): base[tuple(idx)] = v obj = cls.from_voigt(base) if 6 in shape else cls(base) + if populate: assert structure, "Populate option must include structure input" obj = obj.populate(structure, vsym=vsym, verbose=verbose) elif structure: obj = obj.fit_to_structure(structure) + return obj def populate( @@ -652,7 +657,7 @@ def as_dict(self, voigt: bool = False) -> dict: return dct @classmethod - def from_dict(cls, d) -> Tensor: + def from_dict(cls, d: dict) -> Tensor: """Instantiate Tensors from dicts (using MSONable API). Returns: @@ -669,8 +674,10 @@ class TensorCollection(collections.abc.Sequence, MSONable): """ def __init__(self, tensor_list: Sequence, base_class=Tensor) -> None: - """:param tensor_list: List of tensors. - :param base_class: Class to be used. + """ + Args: + tensor_list: List of tensors. + base_class: Class to be used. """ self.tensors = [tensor if isinstance(tensor, base_class) else base_class(tensor) for tensor in tensor_list] @@ -684,7 +691,9 @@ def __iter__(self): return iter(self.tensors) def zeroed(self, tol: float = 1e-3): - """:param tol: Tolerance + """ + Args: + tol: Tolerance. Returns: TensorCollection where small values are set to 0. @@ -694,7 +703,8 @@ def zeroed(self, tol: float = 1e-3): def transform(self, symm_op): """Transforms TensorCollection with a symmetry operation. - :param symm_op: SymmetryOperation. + Args: + symm_op: SymmetryOperation. Returns: TensorCollection. @@ -704,8 +714,9 @@ def transform(self, symm_op): def rotate(self, matrix, tol: float = 1e-3): """Rotates TensorCollection. - :param matrix: Rotation matrix. - :param tol: tolerance. + Args: + matrix: Rotation matrix. + tol: tolerance. Returns: TensorCollection. @@ -717,8 +728,10 @@ def symmetrized(self): """TensorCollection where all tensors are symmetrized.""" return type(self)([tensor.symmetrized for tensor in self]) - def is_symmetric(self, tol: float = 1e-5): - """:param tol: tolerance + def is_symmetric(self, tol: float = 1e-5) -> bool: + """ + Args: + tol: tolerance. Returns: Whether all tensors are symmetric. @@ -728,8 +741,9 @@ def is_symmetric(self, tol: float = 1e-5): def fit_to_structure(self, structure: Structure, symprec: float = 0.1): """Fits all tensors to a Structure. - :param structure: Structure - :param symprec: symmetry precision. + Args: + structure: Structure + symprec: symmetry precision. Returns: TensorCollection. @@ -737,8 +751,10 @@ def fit_to_structure(self, structure: Structure, symprec: float = 0.1): return type(self)([tensor.fit_to_structure(structure, symprec) for tensor in self]) def is_fit_to_structure(self, structure: Structure, tol: float = 1e-2): - """:param structure: Structure - :param tol: tolerance + """ + Args: + structure: Structure + tol: tolerance. Returns: Whether all tensors are fitted to Structure. @@ -755,8 +771,10 @@ def ranks(self): """Ranks for all tensors.""" return [tensor.rank for tensor in self] - def is_voigt_symmetric(self, tol: float = 1e-6): - """:param tol: tolerance + def is_voigt_symmetric(self, tol: float = 1e-6) -> bool: + """ + Args: + tol: tolerance. Returns: Whether all tensors are voigt symmetric. @@ -764,11 +782,12 @@ def is_voigt_symmetric(self, tol: float = 1e-6): return all(tensor.is_voigt_symmetric(tol) for tensor in self) @classmethod - def from_voigt(cls, voigt_input_list, base_class=Tensor): + def from_voigt(cls, voigt_input_list, base_class=Tensor) -> Self: """Creates TensorCollection from voigt form. - :param voigt_input_list: List of voigt tensors - :param base_class: Class for tensor. + Args: + voigt_input_list: List of voigt tensors + base_class: Class for tensor. Returns: TensorCollection. @@ -778,9 +797,10 @@ def from_voigt(cls, voigt_input_list, base_class=Tensor): def convert_to_ieee(self, structure: Structure, initial_fit=True, refine_rotation=True): """Convert all tensors to IEEE. - :param structure: Structure - :param initial_fit: Whether to perform an initial fit. - :param refine_rotation: Whether to refine the rotation. + Args: + structure: Structure + initial_fit: Whether to perform an initial fit. + refine_rotation: Whether to refine the rotation. Returns: TensorCollection. @@ -790,8 +810,9 @@ def convert_to_ieee(self, structure: Structure, initial_fit=True, refine_rotatio def round(self, *args, **kwargs): """Round all tensors. - :param args: Passthrough to Tensor.round - :param kwargs: Passthrough to Tensor.round + Args: + args: Passthrough to Tensor.round + kwargs: Passthrough to Tensor.round Returns: TensorCollection. @@ -804,7 +825,9 @@ def voigt_symmetrized(self): return type(self)([tensor.voigt_symmetrized for tensor in self]) def as_dict(self, voigt=False): - """:param voigt: Whether to use Voigt form. + """ + Args: + voigt: Whether to use Voigt form. Returns: Dict representation of TensorCollection. @@ -823,7 +846,8 @@ def as_dict(self, voigt=False): def from_dict(cls, dct: dict) -> TensorCollection: """Creates TensorCollection from dict. - :param dct: dict + Args: + dct: dict Returns: TensorCollection diff --git a/pymatgen/core/trajectory.py b/pymatgen/core/trajectory.py index e7c32dcb59c..90d48393b64 100644 --- a/pymatgen/core/trajectory.py +++ b/pymatgen/core/trajectory.py @@ -306,7 +306,7 @@ def __getitem__(self, frames: int | slice | list[int]) -> Molecule | Structure | Args: frames: Indices of the trajectory to return. - Return: + Returns: Subset of trajectory """ # Convert to position mode if not already diff --git a/pymatgen/electronic_structure/boltztrap.py b/pymatgen/electronic_structure/boltztrap.py index cf9333300ce..00f8b54c618 100644 --- a/pymatgen/electronic_structure/boltztrap.py +++ b/pymatgen/electronic_structure/boltztrap.py @@ -9,7 +9,7 @@ You need version 1.2.3 or higher -References are:: +References are: Madsen, G. K. H., and Singh, D. J. (2006). BoltzTraP. A code for calculating band-structure dependent quantities. @@ -252,7 +252,8 @@ def nelec(self): def write_energy(self, output_file) -> None: """Writes the energy to an output file. - :param output_file: Filename + Args: + output_file: Filename """ with open(output_file, mode="w") as file: file.write("test\n") @@ -304,7 +305,8 @@ def write_energy(self, output_file) -> None: def write_struct(self, output_file) -> None: """Writes the structure to an output file. - :param output_file: Filename + Args: + output_file: Filename """ if self._symprec is not None: sym = SpacegroupAnalyzer(self._bs.structure, symprec=self._symprec) @@ -338,7 +340,8 @@ def write_struct(self, output_file) -> None: def write_def(self, output_file) -> None: """Writes the def to an output file. - :param output_file: Filename + Args: + output_file: Filename """ # This function is useless in std version of BoltzTraP code # because x_trans script overwrite BoltzTraP.def @@ -363,10 +366,12 @@ def write_def(self, output_file) -> None: "'formatted',0\n" ) - def write_proj(self, output_file_proj, output_file_def) -> None: + def write_proj(self, output_file_proj: str, output_file_def: str) -> None: """Writes the projections to an output file. - :param output_file: Filename + Args: + output_file_proj: output file name + output_file_def: output file name """ # This function is useless in std version of BoltzTraP code # because x_trans script overwrite BoltzTraP.def @@ -421,7 +426,8 @@ def write_proj(self, output_file_proj, output_file_def) -> None: def write_intrans(self, output_file) -> None: """Writes the intrans to an output file. - :param output_file: Filename + Args: + output_file: Filename """ setgap = 1 if self.scissor > 0.0001 else 0 @@ -497,7 +503,8 @@ def write_intrans(self, output_file) -> None: def write_input(self, output_dir) -> None: """Writes the input files. - :param output_dir: Directory to write the input files. + Args: + output_dir: Directory to write the input files. """ if self._bs.is_spin_polarized or self.soc: self.write_energy(f"{output_dir}/boltztrap.energyso") @@ -892,6 +899,7 @@ def check_acc_bzt_bands(sbs_bz, sbs_ref, warn_thr=(0.03, 0.03)): around the gap (semiconductors) or Fermi level (metals). warn_thr is a threshold to get a warning in the accuracy of Boltztap interpolated bands. + Return a dictionary with these keys: - "N": the index of the band compared; inside each there are: - "Corr": correlation coefficient for the 8 compared bands @@ -1614,7 +1622,9 @@ def get_complete_dos(self, structure: Structure, analyzer_for_second_spin=None): return CompleteDos(structure, total_dos=total_dos, pdoss=pdoss) def get_mu_bounds(self, temp=300): - """:param temp: Temperature. + """ + Args: + temp: Temperature. Returns: The chemical potential bounds at that temperature. @@ -1996,7 +2006,9 @@ def as_dict(self): @staticmethod def from_dict(data): - """:param data: Dict representation. + """ + Args: + data: Dict representation. Returns: BoltztrapAnalyzer @@ -2134,7 +2146,10 @@ def _make_float_hall(a): def read_cube_file(filename): - """:param filename: Cube filename + """ + + Args: + filename: Cube filename. Returns: Energy data. diff --git a/pymatgen/electronic_structure/boltztrap2.py b/pymatgen/electronic_structure/boltztrap2.py index 63f4e963a31..c81112e39fb 100644 --- a/pymatgen/electronic_structure/boltztrap2.py +++ b/pymatgen/electronic_structure/boltztrap2.py @@ -853,11 +853,12 @@ def compute_properties_doping(self, doping, temp_r=None) -> None: # """ # Find the mu. - # :param epsilon: - # :param dos: - # :param N0: - # :param T: - # :param dosweight: + # Args: + # epsilon: + # dos: + # N0: + # T: + # dosweight: # """ # delta = np.empty_like(epsilon) # for i, e in enumerate(epsilon): @@ -950,8 +951,13 @@ class BztPlotter: """ def __init__(self, bzt_transP=None, bzt_interp=None) -> None: - """:param bzt_transP: - :param bzt_interp: + """Placeholder. + + TODO: missing docstrings for __init__ + + Args: + bzt_transP (_type_, optional): _description_. Defaults to None. + bzt_interp (_type_, optional): _description_. Defaults to None. """ self.bzt_transP = bzt_transP self.bzt_interp = bzt_interp @@ -1160,10 +1166,11 @@ def merge_up_down_doses(dos_up, dos_dn): """Merge the up and down DOSs. Args: - dos_up: Up DOS. - dos_dn: Down DOS - Return: - CompleteDos object + dos_up: Up DOS. + dos_dn: Down DOS + + Returns: + CompleteDos object """ warnings.warn("This function is not useful anymore. VasprunBSLoader deals with spin case.") cdos = Dos( diff --git a/pymatgen/electronic_structure/cohp.py b/pymatgen/electronic_structure/cohp.py index bbaf42c241e..86cb0a8ec21 100644 --- a/pymatgen/electronic_structure/cohp.py +++ b/pymatgen/electronic_structure/cohp.py @@ -1004,7 +1004,7 @@ def icohpvalue_orbital(self, orbitals, spin=Spin.up): @property def icohp(self): """Dict with icohps for spinup and spindown - Return: + Returns: dict={Spin.up: icohpvalue for spin.up, Spin.down: icohpvalue for spin.down}. """ return self._icohp diff --git a/pymatgen/electronic_structure/core.py b/pymatgen/electronic_structure/core.py index 9a24948aa36..530c18916e8 100644 --- a/pymatgen/electronic_structure/core.py +++ b/pymatgen/electronic_structure/core.py @@ -13,6 +13,8 @@ if TYPE_CHECKING: from collections.abc import Sequence + from typing_extensions import Self + @unique class Spin(Enum): @@ -125,9 +127,11 @@ class Magmom(MSONable): def __init__( self, moment: float | Sequence[float] | np.ndarray | Magmom, saxis: Sequence[float] = (0, 0, 1) ) -> None: - """:param moment: magnetic moment, supplied as float or list/np.ndarray - :param saxis: spin axis, supplied as list/np.ndarray, parameter will - be converted to unit vector (default is [0, 0, 1]) + """ + Args: + moment: magnetic moment, supplied as float or list/np.ndarray + saxis: spin axis, supplied as list/np.ndarray, parameter will + be converted to unit vector (default is [0, 0, 1]). Returns: Magmom object @@ -148,7 +152,7 @@ def __init__( self.saxis = saxis / np.linalg.norm(saxis) @classmethod - def from_global_moment_and_saxis(cls, global_moment, saxis): + def from_global_moment_and_saxis(cls, global_moment, saxis) -> Self: """Convenience method to initialize Magmom from a given global magnetic moment, i.e. magnetic moment with saxis=(0,0,1), and provided saxis. @@ -156,8 +160,9 @@ def from_global_moment_and_saxis(cls, global_moment, saxis): Method is useful if you do not know the components of your magnetic moment in frame of your desired saxis. - :param global_moment: - :param saxis: desired saxis + Args: + global_moment: global magnetic moment + saxis: desired saxis """ magmom = Magmom(global_moment) return cls(magmom.get_moment(saxis=saxis), saxis=saxis) @@ -204,7 +209,8 @@ def get_moment(self, saxis=(0, 0, 1)): Magmom's internal spin quantization axis, i.e. equivalent to Magmom.moment. - :param saxis: (list/numpy array) spin quantization axis + Args: + saxis: (list/numpy array) spin quantization axis Returns: np.ndarray of length 3 @@ -295,7 +301,8 @@ def have_consistent_saxis(magmoms) -> bool: If saxis are inconsistent, can create consistent set with: Magmom.get_consistent_set(magmoms). - :param magmoms: list of magmoms (Magmoms, scalars or vectors) + Args: + magmoms: list of magmoms (Magmoms, scalars or vectors) Returns: bool @@ -312,8 +319,9 @@ def get_consistent_set_and_saxis(magmoms, saxis=None): """Method to ensure a list of magmoms use the same spin axis. Returns a tuple of a list of Magmoms and their global spin axis. - :param magmoms: list of magmoms (Magmoms, scalars or vectors) - :param saxis: can provide a specific global spin axis + Args: + magmoms: list of magmoms (Magmoms, scalars or vectors) + saxis: can provide a specific global spin axis Returns: (list of Magmoms, global spin axis) tuple @@ -330,7 +338,8 @@ def get_suggested_saxis(magmoms): with collinear spins, this would give a sensible saxis for a ncl calculation. - :param magmoms: list of magmoms (Magmoms, scalars or vectors) + Args: + magmoms: list of magmoms (Magmoms, scalars or vectors) Returns: np.ndarray of length 3 @@ -353,7 +362,9 @@ def get_suggested_saxis(magmoms): def are_collinear(magmoms) -> bool: """Method checks to see if a set of magnetic moments are collinear with each other. - :param magmoms: list of magmoms (Magmoms, scalars or vectors). + + Args: + magmoms: list of magmoms (Magmoms, scalars or vectors). Returns: bool. @@ -380,8 +391,10 @@ def from_moment_relative_to_crystal_axes(cls, moment, lattice): relative to crystal axes. Used for obtaining moments from magCIF file. - :param moment: list of floats specifying vector magmom - :param lattice: Lattice + + Args: + moment: list of floats specifying vector magmom + lattice: Lattice Returns: Magmom @@ -397,7 +410,8 @@ def get_moment_relative_to_crystal_axes(self, lattice): """If scalar magmoms, moments will be given arbitrarily along z. Used for writing moments to magCIF file. - :param lattice: Lattice + Args: + lattice: Lattice Returns: vector as list of floats diff --git a/pymatgen/entries/compatibility.py b/pymatgen/entries/compatibility.py index e6c70c2dd5f..71a2477e8bc 100644 --- a/pymatgen/entries/compatibility.py +++ b/pymatgen/entries/compatibility.py @@ -194,7 +194,9 @@ def __init__(self, config_file): self.cpd_energies = c["Advanced"]["CompoundEnergies"] def get_correction(self, entry) -> ufloat: - """:param entry: A ComputedEntry/ComputedStructureEntry + """ + Args: + entry: A ComputedEntry/ComputedStructureEntry. Returns: Correction. @@ -235,7 +237,9 @@ def __init__(self, config_file, correct_peroxide=True): self.correct_peroxide = correct_peroxide def get_correction(self, entry) -> ufloat: - """:param entry: A ComputedEntry/ComputedStructureEntry + """ + Args: + entry: A ComputedEntry/ComputedStructureEntry. Returns: Correction. @@ -332,7 +336,9 @@ def __init__(self, config_file, error_file=None): self.cpd_errors = defaultdict(float) def get_correction(self, entry) -> ufloat: - """:param entry: A ComputedEntry/ComputedStructureEntry + """ + Args: + entry: A ComputedEntry/ComputedStructureEntry. Returns: Correction, Uncertainty. @@ -446,7 +452,9 @@ def __init__(self, config_file, input_set, compat_type, error_file=None): self.u_errors = {} def get_correction(self, entry) -> ufloat: - """:param entry: A ComputedEntry/ComputedStructureEntry + """ + Args: + entry: A ComputedEntry/ComputedStructureEntry. Returns: Correction, Uncertainty. diff --git a/pymatgen/entries/computed_entries.py b/pymatgen/entries/computed_entries.py index 26f22a37d1d..e059c8e07c9 100644 --- a/pymatgen/entries/computed_entries.py +++ b/pymatgen/entries/computed_entries.py @@ -130,10 +130,12 @@ def explain(self): """Return an explanation of how the energy adjustment is calculated.""" return f"{self.description} ({self.value:.3f} eV)" - def normalize(self, factor): + def normalize(self, factor: float) -> None: """Normalize energy adjustment (in place), dividing value/uncertainty by a factor. - :param factor: factor to divide by. + + Args: + factor: factor to divide by. """ self._value /= factor self._uncertainty /= factor @@ -200,10 +202,12 @@ def explain(self): """Return an explanation of how the energy adjustment is calculated.""" return f"{self.description} ({self._adj_per_atom:.3f} eV/atom x {self.n_atoms} atoms)" - def normalize(self, factor): + def normalize(self, factor: float) -> None: """Normalize energy adjustment (in place), dividing value/uncertainty by a factor. - :param factor: factor to divide by. + + Args: + factor: factor to divide by. """ self.n_atoms /= factor @@ -259,10 +263,12 @@ def explain(self): """Return an explanation of how the energy adjustment is calculated.""" return f"{self.description} ({self._adj_per_deg:.4f} eV/K/atom x {self.temp} K x {self.n_atoms} atoms)" - def normalize(self, factor): + def normalize(self, factor: float) -> None: """Normalize energy adjustment (in place), dividing value/uncertainty by a factor. - :param factor: factor to divide by. + + Args: + factor: factor to divide by. """ self.n_atoms /= factor @@ -468,7 +474,9 @@ def __eq__(self, other: object) -> bool: @classmethod def from_dict(cls, d) -> ComputedEntry: - """:param d: Dict representation. + """ + Args: + d: Dict representation. Returns: ComputedEntry @@ -603,7 +611,9 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d) -> ComputedStructureEntry: - """:param d: Dict representation. + """ + Args: + d: Dict representation. Returns: ComputedStructureEntry @@ -935,7 +945,9 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, d) -> GibbsComputedStructureEntry: - """:param d: Dict representation. + """ + Args: + d: Dict representation. Returns: GibbsComputedStructureEntry diff --git a/pymatgen/entries/entry_tools.py b/pymatgen/entries/entry_tools.py index 8e431050c58..c23c895b279 100644 --- a/pymatgen/entries/entry_tools.py +++ b/pymatgen/entries/entry_tools.py @@ -211,14 +211,16 @@ def __len__(self): def add(self, element): """Add an entry. - :param element: Entry + Args: + element: Entry """ self.entries.add(element) def discard(self, element): """Discard an entry. - :param element: Entry + Args: + element: Entry """ self.entries.discard(element) diff --git a/pymatgen/entries/exp_entries.py b/pymatgen/entries/exp_entries.py index 8ac366c0d0a..c32e01dda3b 100644 --- a/pymatgen/entries/exp_entries.py +++ b/pymatgen/entries/exp_entries.py @@ -52,7 +52,9 @@ def __repr__(self): @classmethod def from_dict(cls, d): - """:param d: Dict representation. + """ + Args: + d: Dict representation. Returns: ExpEntry diff --git a/pymatgen/ext/cod.py b/pymatgen/ext/cod.py index 9e4d55a31e3..b407c5eb254 100644 --- a/pymatgen/ext/cod.py +++ b/pymatgen/ext/cod.py @@ -47,7 +47,8 @@ class COD: def query(self, sql: str) -> str: """Perform a query. - :param sql: SQL string + Args: + sql: SQL string Returns: Response from SQL query. diff --git a/pymatgen/io/abinit/abitimer.py b/pymatgen/io/abinit/abitimer.py index a1105df7d8c..68404f5f324 100644 --- a/pymatgen/io/abinit/abitimer.py +++ b/pymatgen/io/abinit/abitimer.py @@ -66,10 +66,11 @@ def walk(cls, top=".", ext=".abo"): Scan directory tree starting from top, look for files with extension `ext` and parse timing data. - Return: (parser, paths, okfiles) - where `parser` is the new object, `paths` is the list of files found and `okfiles` - is the list of files that have been parsed successfully. - (okfiles == paths) if all files have been parsed. + Returns: + parser: the new object + paths: the list of files found + okfiles: list of files that have been parsed successfully. + (okfiles == paths) if all files have been parsed. """ paths = [] for root, _dirs, files in os.walk(top): @@ -106,7 +107,8 @@ def parse(self, filenames): Read and parse a filename or a list of filenames. Files that cannot be opened are ignored. A single filename may also be given. - Return: list of successfully read files. + Returns: + list of successfully read files. """ if isinstance(filenames, str): filenames = [filenames] @@ -251,7 +253,8 @@ def pefficiency(self): """ Analyze the parallel efficiency. - Return: ParallelEfficiency object. + Returns: + ParallelEfficiency object. """ timers = self.timers() diff --git a/pymatgen/io/abinit/inputs.py b/pymatgen/io/abinit/inputs.py index be48629cb11..7f918357172 100644 --- a/pymatgen/io/abinit/inputs.py +++ b/pymatgen/io/abinit/inputs.py @@ -481,33 +481,33 @@ def calc_shiftk(structure, symprec: float = 0.01, angle_tolerance=5): This is often the preferred k point sampling. For a non-shifted Monkhorst-Pack grid, use `nshiftk=1` and `shiftk 0.0 0.0 0.0`, but there is little reason to do that. - When the primitive vectors of the lattice form a FCC lattice, with rprim:: + When the primitive vectors of the lattice form a FCC lattice, with rprim: 0.0 0.5 0.5 0.5 0.0 0.5 0.5 0.5 0.0 - the (very efficient) usual Monkhorst-Pack sampling will be generated by using nshiftk= 4 and shiftk:: + the (very efficient) usual Monkhorst-Pack sampling will be generated by using nshiftk= 4 and shiftk: 0.5 0.5 0.5 0.5 0.0 0.0 0.0 0.5 0.0 0.0 0.0 0.5 - When the primitive vectors of the lattice form a BCC lattice, with rprim:: + When the primitive vectors of the lattice form a BCC lattice, with rprim: -0.5 0.5 0.5 0.5 -0.5 0.5 0.5 0.5 -0.5 - the usual Monkhorst-Pack sampling will be generated by using nshiftk= 2 and shiftk:: + the usual Monkhorst-Pack sampling will be generated by using nshiftk= 2 and shiftk: 0.25 0.25 0.25 -0.25 -0.25 -0.25 However, the simple sampling nshiftk=1 and shiftk 0.5 0.5 0.5 is excellent. - For hexagonal lattices with hexagonal axes, e.g. rprim:: + For hexagonal lattices with hexagonal axes, e.g. rprim: 1.0 0.0 0.0 -0.5 sqrt(3)/2 0.0 @@ -1035,8 +1035,7 @@ class BasicMultiDataset: multi.get("paral_kgb", 0) - .. warning:: - + Warning: BasicMultiDataset does not support calculations done with different sets of pseudopotentials. The inputs can have different crystalline structures (as long as the atom types are equal) but each input in BasicMultiDataset must have the same set of pseudopotentials. diff --git a/pymatgen/io/abinit/pseudos.py b/pymatgen/io/abinit/pseudos.py index 7c39d198f1d..f4826b47d3d 100644 --- a/pymatgen/io/abinit/pseudos.py +++ b/pymatgen/io/abinit/pseudos.py @@ -998,8 +998,7 @@ class PseudoParser: """ Responsible for parsing pseudopotential files and returning pseudopotential objects. - Usage:: - + Usage: pseudo = PseudoParser().parse("filename") """ @@ -1555,7 +1554,8 @@ def from_dir(cls, top, exts=None, exclude_dirs="_*"): we try to open all files in top exclude_dirs: Wildcard used to exclude directories. - return: PseudoTable sorted by atomic number Z. + Returns: + PseudoTable sorted by atomic number Z. """ pseudos = [] @@ -1698,8 +1698,7 @@ def all_combinations_for_elements(self, element_symbols): for the given list of element_symbols. Each item is a list of pseudopotential objects. - Example:: - + Example: table.all_combinations_for_elements(["Li", "F"]) """ dct = {} @@ -1827,8 +1826,8 @@ def sorted(self, attrname, reverse=False): """ Sort the table according to the value of attribute attrname. - Return: - New class:`PseudoTable` object + Returns: + New class: `PseudoTable` object """ attrs = [] for i, pseudo in self: diff --git a/pymatgen/io/atat.py b/pymatgen/io/atat.py index 31860a4c9a3..7badcab828c 100644 --- a/pymatgen/io/atat.py +++ b/pymatgen/io/atat.py @@ -60,7 +60,8 @@ def structure_from_str(data): Parses a rndstr.in, lat.in or bestsqs.out file into pymatgen's Structure format. - :param data: contents of a rndstr.in, lat.in or bestsqs.out file + Args: + data: contents of a rndstr.in, lat.in or bestsqs.out file Returns: Structure object diff --git a/pymatgen/io/babel.py b/pymatgen/io/babel.py index 8704d3a3149..262f9b7c17b 100644 --- a/pymatgen/io/babel.py +++ b/pymatgen/io/babel.py @@ -8,6 +8,7 @@ import copy import warnings +from typing import TYPE_CHECKING from monty.dev import requires @@ -18,6 +19,11 @@ except Exception: openbabel = None +if TYPE_CHECKING: + from typing_extensions import Self + + from pymatgen.analysis.graphs import MoleculeGraph + __author__ = "Shyue Ping Ong, Qi Wang" __copyright__ = "Copyright 2012, The Materials Project" @@ -82,7 +88,7 @@ def __init__(self, mol: Molecule | openbabel.OBMol | pybel.Molecule) -> None: raise ValueError(f"Unsupported input type {type(mol)}, must be Molecule, openbabel.OBMol or pybel.Molecule") @property - def pymatgen_mol(self): + def pymatgen_mol(self) -> Molecule: """Returns pymatgen Molecule object.""" sp = [] coords = [] @@ -96,7 +102,7 @@ def openbabel_mol(self): """Returns OpenBabel's OBMol.""" return self._ob_mol - def localopt(self, forcefield="mmff94", steps=500): + def localopt(self, forcefield: str = "mmff94", steps: int = 500) -> None: """ A wrapper to pybel's localopt method to optimize a Molecule. @@ -109,7 +115,7 @@ def localopt(self, forcefield="mmff94", steps=500): pybelmol.localopt(forcefield=forcefield, steps=steps) self._ob_mol = pybelmol.OBMol - def make3d(self, forcefield="mmff94", steps=50): + def make3d(self, forcefield: str = "mmff94", steps: int = 50) -> None: """ A wrapper to pybel's make3D method generate a 3D structure from a 2D or 0D structure. @@ -132,11 +138,11 @@ def make3d(self, forcefield="mmff94", steps=50): pybelmol.make3D(forcefield=forcefield, steps=steps) self._ob_mol = pybelmol.OBMol - def add_hydrogen(self): + def add_hydrogen(self) -> None: """Add hydrogens (make all hydrogen explicit).""" self._ob_mol.AddHydrogens() - def remove_bond(self, idx1, idx2): + def remove_bond(self, idx1: int, idx2: int) -> None: """ Remove a bond from an openbabel molecule. @@ -150,7 +156,7 @@ def remove_bond(self, idx1, idx2): ): self._ob_mol.DeleteBond(obbond) - def rotor_conformer(self, *rotor_args, algo="WeightedRotorSearch", forcefield="mmff94"): + def rotor_conformer(self, *rotor_args, algo: str = "WeightedRotorSearch", forcefield: str = "mmff94") -> None: """ Conformer search based on several Rotor Search algorithms of openbabel. If the input molecule is not 3D, make3d will be called (generate 3D @@ -200,7 +206,7 @@ def rotor_conformer(self, *rotor_args, algo="WeightedRotorSearch", forcefield="m rotor_search(*rotor_args) ff.GetConformers(self._ob_mol) - def gen3d_conformer(self): + def gen3d_conformer(self) -> None: """ A combined method to first generate 3D structures from 0D or 2D structures and then find the minimum energy conformer: @@ -225,13 +231,13 @@ def gen3d_conformer(self): def confab_conformers( self, - forcefield="mmff94", - freeze_atoms=None, - rmsd_cutoff=0.5, - energy_cutoff=50.0, - conf_cutoff=100000, - verbose=False, - ): + forcefield: str = "mmff94", + freeze_atoms: list[int] | None = None, + rmsd_cutoff: float = 0.5, + energy_cutoff: float = 50.0, + conf_cutoff: int = 100000, + verbose: bool = False, + ) -> list[Molecule]: """ Conformer generation based on Confab to generate all diverse low-energy conformers for molecules. This is different from rotor_conformer or @@ -245,7 +251,7 @@ def confab_conformers( conformer search, default is None. rmsd_cutoff (float): rmsd_cufoff, default is 0.5 Angstrom. energy_cutoff (float): energy_cutoff, default is 50.0 kcal/mol. - conf_cutoff (float): max number of conformers to test, + conf_cutoff (int): max number of conformers to test, default is 1 million. verbose (bool): whether to display information on torsions found, default is False. @@ -289,11 +295,11 @@ def confab_conformers( return conformers @property - def pybel_mol(self): + def pybel_mol(self) -> Molecule: """Returns Pybel's Molecule object.""" return pybel.Molecule(self._ob_mol) - def write_file(self, filename, file_format="xyz"): + def write_file(self, filename: str, file_format: str = "xyz") -> None: """ Uses OpenBabel to output all supported formats. @@ -302,10 +308,12 @@ def write_file(self, filename, file_format="xyz"): file_format: String specifying any OpenBabel supported formats. """ mol = pybel.Molecule(self._ob_mol) - return mol.write(file_format, filename, overwrite=True) + mol.write(file_format, filename, overwrite=True) @classmethod - def from_file(cls, filename, file_format="xyz", return_all_molecules=False): + def from_file( + cls, filename: str, file_format: str = "xyz", return_all_molecules: bool = False + ) -> Self | list[Self]: """ Uses OpenBabel to read a molecule from a file in all supported formats. @@ -326,7 +334,7 @@ def from_file(cls, filename, file_format="xyz", return_all_molecules=False): return cls(next(mols).OBMol) @staticmethod - def from_molecule_graph(mol): + def from_molecule_graph(mol: MoleculeGraph) -> BabelMolAdaptor: """ Read a molecule from a pymatgen MoleculeGraph object. @@ -340,7 +348,7 @@ def from_molecule_graph(mol): @needs_openbabel @classmethod - def from_str(cls, string_data, file_format="xyz"): + def from_str(cls, string_data: str, file_format: str = "xyz") -> Self: """ Uses OpenBabel to read a molecule from a string in all supported formats. @@ -352,5 +360,5 @@ def from_str(cls, string_data, file_format="xyz"): Returns: BabelMolAdaptor object """ - mols = pybel.readstring(str(file_format), str(string_data)) + mols = pybel.readstring(file_format, string_data) return cls(mols.OBMol) diff --git a/pymatgen/io/cif.py b/pymatgen/io/cif.py index e155d4d5e52..d5456a8d747 100644 --- a/pymatgen/io/cif.py +++ b/pymatgen/io/cif.py @@ -172,7 +172,8 @@ def from_str(cls, string): """ Reads CifBlock from string. - :param string: String representation. + Args: + string: String representation. Returns: CifBlock @@ -237,7 +238,8 @@ def __str__(self): def from_str(cls, string) -> CifFile: """Reads CifFile from a string. - :param string: String representation. + Args: + string: String representation. Returns: CifFile @@ -264,7 +266,8 @@ def from_file(cls, filename: str | Path) -> CifFile: """ Reads CifFile from a filename. - :param filename: Filename + Args: + filename: Filename Returns: CifFile @@ -384,16 +387,16 @@ def _sanitize_data(self, data): This function is here so that CifParser can assume its input conforms to spec, simplifying its implementation. - :param data: CifBlock + + Args: + data: CifBlock Returns: data CifBlock """ - """ - This part of the code deals with handling formats of data as found in - CIF files extracted from the Springer Materials/Pauling File - databases, and that are different from standard ICSD formats. - """ + # This part of the code deals with handling formats of data as found in + # CIF files extracted from the Springer Materials/Pauling File + # databases, and that are different from standard ICSD formats. # check for implicit hydrogens, warn if any present if "_atom_site_attached_hydrogens" in data.data: attached_hydrogens = [str2float(x) for x in data.data["_atom_site_attached_hydrogens"] if str2float(x) != 0] @@ -1232,10 +1235,12 @@ def parse_structures( raise ValueError("Invalid CIF file with no structures!") return structures - def get_bibtex_string(self): + def get_bibtex_string(self) -> str: """ Get BibTeX reference from CIF file. - :param data: + + args: + data: Returns: BibTeX string. diff --git a/pymatgen/io/common.py b/pymatgen/io/common.py index 8cc909d32b1..21913435a24 100644 --- a/pymatgen/io/common.py +++ b/pymatgen/io/common.py @@ -306,7 +306,8 @@ def from_hdf5(cls, filename, **kwargs): """ Reads VolumetricData from HDF5 file. - :param filename: Filename + Args: + filename: Filename Returns: VolumetricData diff --git a/pymatgen/io/exciting/inputs.py b/pymatgen/io/exciting/inputs.py index 794ec56f255..7544edf735f 100644 --- a/pymatgen/io/exciting/inputs.py +++ b/pymatgen/io/exciting/inputs.py @@ -135,7 +135,8 @@ def from_str(cls, data): @classmethod def from_file(cls, filename): """ - :param filename: Filename + Args: + filename: Filename Returns: ExcitingInput @@ -325,8 +326,9 @@ def _indent(elem, level=0): """ Helper method to indent elements. - :param elem: - :param level: + Args: + elem: + level: """ i = "\n" + level * " " if len(elem): diff --git a/pymatgen/io/feff/inputs.py b/pymatgen/io/feff/inputs.py index 293c096cede..d51a75034ce 100644 --- a/pymatgen/io/feff/inputs.py +++ b/pymatgen/io/feff/inputs.py @@ -143,7 +143,7 @@ class Header(MSONable): """ Creates Header for the FEFF input file. - Has the following format:: + Has the following format: * This feff.inp file generated by pymatgen, materialsproject.org TITLE comment: @@ -539,7 +539,7 @@ def __setitem__(self, key, val): Feff tags. Also cleans the parameter and val by stripping leading and trailing white spaces. - Arg: + Args: key: dict key value value: value associated with key in dictionary """ @@ -857,7 +857,7 @@ def pot_dict_from_str(pot_data): Creates atomic symbol/potential number dictionary forward and reverse. - Arg: + Args: pot_data: potential data in string format Returns: diff --git a/pymatgen/io/fiesta.py b/pymatgen/io/fiesta.py index 05f8c2ac262..b26ee3d8f5e 100644 --- a/pymatgen/io/fiesta.py +++ b/pymatgen/io/fiesta.py @@ -15,12 +15,18 @@ import shutil import subprocess from string import Template +from typing import TYPE_CHECKING from monty.io import zopen from monty.json import MSONable from pymatgen.core.structure import Molecule +if TYPE_CHECKING: + from pathlib import Path + + from typing_extensions import Self + __author__ = "ndardenne" __copyright__ = "Copyright 2012, The Materials Project" __version__ = "0.1" @@ -82,9 +88,10 @@ def as_dict(self): } @classmethod - def from_dict(cls, d): + def from_dict(cls, d: dict) -> Self: """ - :param d: Dict representation. + Args: + d: Dict representation. Returns: Nwchem2Fiesta @@ -180,9 +187,10 @@ def as_dict(self): } @classmethod - def from_dict(cls, d): + def from_dict(cls, d: dict) -> Self: """ - :param d: Dict representation + Args: + d: Dict representation Returns: FiestaRun @@ -301,12 +309,13 @@ def __init__( bse_tddft_options: dict[str, str] | None = None, ): """ - :param mol: pymatgen mol - :param correlation_grid: dict - :param Exc_DFT_option: dict - :param COHSEX_options: dict - :param GW_options: dict - :param BSE_TDDFT_options: dict + Args: + mol: pymatgen mol + correlation_grid: dict + Exc_DFT_option: dict + COHSEX_options: dict + GW_options: dict + BSE_TDDFT_options: dict """ self._mol = mol self.correlation_grid = correlation_grid or {"dE_grid": "0.500", "n_grid": "14"} @@ -333,9 +342,11 @@ def __init__( def set_auxiliary_basis_set(self, folder, auxiliary_folder, auxiliary_basis_set_type="aug_cc_pvtz"): """ copy in the desired folder the needed auxiliary basis set "X2.ion" where X is a specie. - :param auxiliary_folder: folder where the auxiliary basis sets are stored - :param auxiliary_basis_set_type: type of basis set (string to be found in the extension of the file name; must - be in lower case). ex: C2.ion_aug_cc_pvtz_RI_Weigend find "aug_cc_pvtz". + + Args: + auxiliary_folder: folder where the auxiliary basis sets are stored + auxiliary_basis_set_type: type of basis set (string to be found in the extension of the file name; must + be in lower case). ex: C2.ion_aug_cc_pvtz_RI_Weigend find "aug_cc_pvtz". """ list_files = os.listdir(auxiliary_folder) @@ -347,10 +358,12 @@ def set_auxiliary_basis_set(self, folder, auxiliary_folder, auxiliary_basis_set_ def set_gw_options(self, nv_band=10, nc_band=10, n_iteration=5, n_grid=6, dE_grid=0.5): """ Set parameters in cell.in for a GW computation - :param nv__band: number of valence bands to correct with GW - :param nc_band: number of conduction bands to correct with GW - :param n_iteration: number of iteration - :param n_grid and dE_grid:: number of points and spacing in eV for correlation grid. + + Args: + nv__band: number of valence bands to correct with GW + nc_band: number of conduction bands to correct with GW + n_iteration: number of iteration + n_grid and dE_grid: number of points and spacing in eV for correlation grid. """ self.GW_options.update(nv_corr=nv_band, nc_corr=nc_band, nit_gw=n_iteration) self.correlation_grid.update(dE_grid=dE_grid, n_grid=n_grid) @@ -367,16 +380,19 @@ def make_full_bse_densities_folder(folder): def set_bse_options(self, n_excitations=10, nit_bse=200): """ Set parameters in cell.in for a BSE computation - :param nv_bse: number of valence bands - :param nc_bse: number of conduction bands - :param n_excitations: number of excitations - :param nit_bse: number of iterations. + + Args: + nv_bse: number of valence bands + nc_bse: number of conduction bands + n_excitations: number of excitations + nit_bse: number of iterations. """ self.bse_tddft_options.update(npsi_bse=n_excitations, nit_bse=nit_bse) def dump_bse_data_in_gw_run(self, BSE_dump=True): """ - :param BSE_dump: boolean + Args: + BSE_dump: boolean Returns: set the "do_bse" variable to one in cell.in @@ -386,14 +402,15 @@ def dump_bse_data_in_gw_run(self, BSE_dump=True): else: self.bse_tddft_options.update(do_bse=0, do_tddft=0) - def dump_tddft_data_in_gw_run(self, tddft_dump=True): + def dump_tddft_data_in_gw_run(self, tddft_dump: bool = True): """ - :param TDDFT_dump: boolean + Args: + TDDFT_dump: boolean Returns: set the do_tddft variable to one in cell.in """ - self.bse_tddft_options.update(do_bse=0, do_tddft=1 if tddft_dump else 0) + self.bse_tddft_options.update(do_bse="0", do_tddft="1" if tddft_dump else "0") @property def infos_on_system(self): @@ -518,10 +535,12 @@ def __str__(self): geometry="\n".join(geometry), ) - def write_file(self, filename): + def write_file(self, filename: str | Path) -> None: """ Write FiestaInput to a file - :param filename: Filename. + + Args: + filename: Filename. """ with zopen(filename, mode="w") as file: file.write(str(self)) @@ -538,24 +557,25 @@ def as_dict(self): } @classmethod - def from_dict(cls, d): + def from_dict(cls, d: dict) -> Self: """ - :param d: Dict representation + Args: + d: Dict representation Returns: FiestaInput """ return cls( - Molecule.from_dict(d["mol"]), + mol=Molecule.from_dict(d["mol"]), correlation_grid=d["correlation_grid"], - Exc_DFT_option=d["Exc_DFT_option"], - COHSEX_options=d["geometry_options"], - GW_options=d["symmetry_options"], - BSE_TDDFT_options=d["memory_options"], + exc_dft_option=d["Exc_DFT_option"], + cohsex_options=d["geometry_options"], + gw_options=d["symmetry_options"], + bse_tddft_options=d["memory_options"], ) @classmethod - def from_str(cls, string_input): + def from_str(cls, string_input: str) -> Self: """ Read an FiestaInput from a string. Currently tested to work with files generated from this class itself. @@ -690,7 +710,7 @@ def from_str(cls, string_input): ) @classmethod - def from_file(cls, filename): + def from_file(cls, filename: str | Path) -> Self: """ Read an Fiesta input from a file. Currently tested to work with files generated from this class itself. diff --git a/pymatgen/io/gaussian.py b/pymatgen/io/gaussian.py index fc0c29320cf..be194aacb4d 100644 --- a/pymatgen/io/gaussian.py +++ b/pymatgen/io/gaussian.py @@ -36,7 +36,7 @@ def read_route_line(route): Args: route (str) : the route line - Return: + Returns: functional (str) : the method (HF, PBE ...) basis_set (str) : the basis set route (dict) : dictionary of parameters @@ -460,7 +460,8 @@ def as_dict(self): @classmethod def from_dict(cls, dct: dict) -> GaussianInput: """ - :param dct: dict + Args: + dct: dict Returns: GaussianInput @@ -551,22 +552,18 @@ class GaussianOutput: printed using `pop=NBOREAD` and `$nbo bndidx $end`. Methods: - .. method:: to_input() - - Return a GaussianInput object using the last geometry and the same - calculation parameters. - - .. method:: read_scan() - - Read a potential energy surface from a gaussian scan calculation. - - .. method:: get_scan_plot() + to_input() + Return a GaussianInput object using the last geometry and the same + calculation parameters. - Get a matplotlib plot of the potential energy surface + read_scan() + Read a potential energy surface from a gaussian scan calculation. - .. method:: save_scan_plot() + get_scan_plot() + Get a matplotlib plot of the potential energy surface - Save a matplotlib plot of the potential energy surface to a file + save_scan_plot() + Save a matplotlib plot of the potential energy surface to a file """ def __init__(self, filename): diff --git a/pymatgen/io/lammps/data.py b/pymatgen/io/lammps/data.py index 554f94bf899..404d808ed45 100644 --- a/pymatgen/io/lammps/data.py +++ b/pymatgen/io/lammps/data.py @@ -294,7 +294,7 @@ def structure(self) -> Structure: Exports a periodic structure object representing the simulation box. - Return: + Returns: Structure """ masses = self.masses @@ -1330,7 +1330,7 @@ def structure(self) -> Structure: Exports a periodic structure object representing the simulation box. - Return: + Returns: Structure """ ld_cp = self.as_lammpsdata() diff --git a/pymatgen/io/lobster/lobsterenv.py b/pymatgen/io/lobster/lobsterenv.py index a15ee86a9ab..bb918fea043 100644 --- a/pymatgen/io/lobster/lobsterenv.py +++ b/pymatgen/io/lobster/lobsterenv.py @@ -79,7 +79,6 @@ def __init__( id_blist_sg2: str = "ICOBI", ) -> None: """ - Args: filename_icohp: (str) Path to ICOHPLIST.lobster or ICOOPLIST.lobster or ICOBILIST.lobster obj_icohp: Icohplist object diff --git a/pymatgen/io/phonopy.py b/pymatgen/io/phonopy.py index 4954bd99071..960fc7006d5 100644 --- a/pymatgen/io/phonopy.py +++ b/pymatgen/io/phonopy.py @@ -86,7 +86,7 @@ def get_structure_from_dict(dct): def eigvec_to_eigdispl(v, q, frac_coords, mass): r""" Converts a single eigenvector to an eigendisplacement in the primitive cell - according to the formula:: + according to the formula: exp(2*pi*i*(frac_coords \\dot q) / sqrt(mass) * v @@ -110,7 +110,7 @@ def get_ph_bs_symm_line_from_dict(bands_dict, has_nac=False, labels_dict=None): extracted by the band.yaml file produced by phonopy. The labels will be extracted from the dictionary, if present. If the 'eigenvector' key is found the eigendisplacements will be calculated according to the - formula:: + formula: exp(2*pi*i*(frac_coords \\dot q) / sqrt(mass) * v @@ -246,7 +246,7 @@ def get_displaced_structures(pmg_structure, atom_disp=0.01, supercell_matrix=Non the outputting displacement yaml file, e.g. disp.yaml. **kwargs: Parameters used in Phonopy.generate_displacement method. - Return: + Returns: A list of symmetrically inequivalent structures with displacements, in which the first element is the perfect supercell structure. """ @@ -479,7 +479,7 @@ def get_gs_ph_bs_symm_line_from_dict( extracted by the gruneisen.yaml file produced by phonopy. The labels will be extracted from the dictionary, if present. If the 'eigenvector' key is found the eigendisplacements will be calculated according to the - formula:: + formula: exp(2*pi*i*(frac_coords \\dot q) / sqrt(mass) * v diff --git a/pymatgen/io/res.py b/pymatgen/io/res.py index 95a25763d7f..ce96ab77bc5 100644 --- a/pymatgen/io/res.py +++ b/pymatgen/io/res.py @@ -503,7 +503,7 @@ def get_airss_version(self) -> tuple[str, date] | None: """ Retrieves the version of AIRSS that was used along with the build date (not compile date). - Return: + Returns: (version string, date) """ for rem in self._res.REMS: diff --git a/pymatgen/io/vasp/inputs.py b/pymatgen/io/vasp/inputs.py index 12e927ca151..b807eda5f5a 100644 --- a/pymatgen/io/vasp/inputs.py +++ b/pymatgen/io/vasp/inputs.py @@ -39,6 +39,7 @@ from collections.abc import Iterator, Sequence from numpy.typing import ArrayLike + from typing_extensions import Self from pymatgen.core.trajectory import Vector3D from pymatgen.util.typing import PathLike @@ -608,7 +609,8 @@ def as_dict(self) -> dict: @classmethod def from_dict(cls, dct: dict) -> Poscar: """ - :param dct: Dict representation. + Args: + dct: Dict representation. Returns: Poscar @@ -1005,7 +1007,8 @@ def __str__(self): @classmethod def from_str(cls, mode: str) -> KpointsSupportedModes: """ - :param s: String + Args: + mode: String Returns: Kpoints_supported_modes @@ -1099,13 +1102,12 @@ def style(self) -> KpointsSupportedModes: return self._style @style.setter - def style(self, style): + def style(self, style) -> None: """ - :param style: Style + Sets the style for the Kpoints. One of Kpoints_supported_modes enum. - Returns: - Sets the style for the Kpoints. One of Kpoints_supported_modes - enum. + Args: + style: Style """ if isinstance(style, str): style = Kpoints.supported_modes.from_str(style) @@ -1518,7 +1520,7 @@ def __repr__(self): lines.append(" ".join(map(str, self.kpts_shift))) return "\n".join(lines) + "\n" - def as_dict(self): + def as_dict(self) -> dict: """MSONable dict.""" dct = { "comment": self.comment, @@ -1545,7 +1547,8 @@ def as_dict(self): @classmethod def from_dict(cls, d): """ - :param d: Dict representation. + Args: + d: Dict representation. Returns: Kpoints @@ -1829,10 +1832,11 @@ def copy(self) -> PotcarSingle: return PotcarSingle(self.data, symbol=self.symbol) @classmethod - def from_file(cls, filename: str) -> PotcarSingle: + def from_file(cls, filename: str) -> Self: """Reads PotcarSingle from file. - :param filename: Filename. + Args: + filename: Filename. Returns: PotcarSingle @@ -2481,9 +2485,10 @@ def as_dict(self): } @classmethod - def from_dict(cls, d): + def from_dict(cls, d) -> Self: """ - :param d: Dict representation + Args: + d: Dict representation Returns: Potcar @@ -2491,11 +2496,12 @@ def from_dict(cls, d): return Potcar(symbols=d["symbols"], functional=d["functional"]) @classmethod - def from_file(cls, filename: str): + def from_file(cls, filename: str) -> Self: """ Reads Potcar from file. - :param filename: Filename + Args: + filename: Filename Returns: Potcar @@ -2616,13 +2622,14 @@ def as_dict(self): @classmethod def from_dict(cls, dct): """ - :param d: Dict representation. + Args: + d: Dict representation. Returns: VaspInput """ dec = MontyDecoder() - sub_dct = {"optional_files": {}} + sub_dct: dict[str, dict] = {"optional_files": {}} for key, val in dct.items(): if key in ["INCAR", "POSCAR", "POTCAR", "KPOINTS"]: sub_dct[key.lower()] = dec.process_decoded(val) @@ -2691,15 +2698,16 @@ def run_vasp( vasp_cmd: list | None = None, output_file: PathLike = "vasp.out", err_file: PathLike = "vasp.err", - ): + ) -> None: """ Write input files and run VASP. - :param run_dir: Where to write input files and do the run. - :param vasp_cmd: Args to be supplied to run VASP. Otherwise, the - PMG_VASP_EXE in .pmgrc.yaml is used. - :param output_file: File to write output. - :param err_file: File to write err. + Args: + run_dir: Where to write input files and do the run. + vasp_cmd: Args to be supplied to run VASP. Otherwise, the + PMG_VASP_EXE in .pmgrc.yaml is used. + output_file: File to write output. + err_file: File to write err. """ self.write_input(output_dir=run_dir) vasp_cmd = vasp_cmd or SETTINGS.get("PMG_VASP_EXE") # type: ignore[assignment] diff --git a/pymatgen/io/vasp/optics.py b/pymatgen/io/vasp/optics.py index efc17ad6ada..7e9c0f1cff5 100644 --- a/pymatgen/io/vasp/optics.py +++ b/pymatgen/io/vasp/optics.py @@ -306,7 +306,7 @@ def get_delta(x0: float, sigma: float, nx: int, dx: float, ismear: int = 3): dx: The gridspacing of the output grid. ismear: The smearing parameter used by the ``step_func``. - Return: + Returns: np.array: Array of size `nx` with delta function on the desired outputgrid. """ xgrid = np.linspace(0, nx * dx, nx, endpoint=False) @@ -330,7 +330,7 @@ def get_step(x0, sigma, nx, dx, ismear): dx: The gridspacing of the output grid. ismear: The smearing parameter used by the ``step_func``. - Return: + Returns: np.array: Array of size `nx` with step function on the desired outputgrid. """ xgrid = np.linspace(0, nx * dx, nx, endpoint=False) @@ -367,7 +367,7 @@ def epsilon_imag( jdir: The second direction of the dielectric tensor mask: Mask for the bands/kpoint/spin index to include in the calculation - Return: + Returns: np.array: Array of size `nedos` with the imaginary part of the dielectric function. """ norm_kweights = np.array(kweights) / np.sum(kweights) @@ -433,7 +433,7 @@ def kramers_kronig( deltae: The energy grid spacing cshift: The shift of the imaginary part of the dielectric function. - Return: + Returns: np.array: Array of size `nedos` with the complex dielectric function. """ egrid = np.linspace(0, deltae * nedos, nedos) diff --git a/pymatgen/io/vasp/outputs.py b/pymatgen/io/vasp/outputs.py index 16256904e0c..1252c0d9170 100644 --- a/pymatgen/io/vasp/outputs.py +++ b/pymatgen/io/vasp/outputs.py @@ -16,7 +16,7 @@ from glob import glob from io import StringIO from pathlib import Path -from typing import Literal +from typing import TYPE_CHECKING, Literal import numpy as np from monty.io import reverse_readfile, zopen @@ -44,6 +44,9 @@ logger = logging.getLogger(__name__) +if TYPE_CHECKING: + from typing_extensions import Self + def _parse_parameters(val_type, val): """ @@ -1162,7 +1165,8 @@ def update_charge_from_potcar(self, path): """ Sets the charge of a structure based on the POTCARs found. - :param path: Path to search for POTCARs + Args: + path: Path to search for POTCARs """ potcar = self.get_potcars(path) @@ -3661,7 +3665,7 @@ def __init__(self, poscar, data, data_aug=None): self._distance_matrix = {} @classmethod - def from_file(cls, filename: str): + def from_file(cls, filename: str) -> Self: """Read a CHGCAR file. Args: @@ -3713,11 +3717,12 @@ def __init__(self, poscar, data): self.data = data @classmethod - def from_file(cls, filename): + def from_file(cls, filename: str) -> Self: """ Reads a ELFCAR file. - :param filename: Filename + Args: + filename: Filename Returns: Elfcar @@ -5023,7 +5028,7 @@ class Waveder(MSONable): cder_imag: np.ndarray @classmethod - def from_formatted(cls, filename): + def from_formatted(cls, filename: str) -> Self: """Reads the WAVEDERF file and returns a Waveder object. Note: This file is only produced when LOPTICS is true AND vasp has been @@ -5053,7 +5058,7 @@ def from_formatted(cls, filename): return cls(cder_real, cder_imag) @classmethod - def from_binary(cls, filename, data_type="complex64"): + def from_binary(cls, filename: str, data_type: str = "complex64") -> Self: """Read the WAVEDER file and returns a Waveder object. Args: @@ -5173,7 +5178,7 @@ def data(self): return self.me_real + 1j * self.me_imag @classmethod - def from_file(cls, filename: str) -> WSWQ: + def from_file(cls, filename: str) -> Self: """Constructs a WSWQ object from a file. Args: diff --git a/pymatgen/symmetry/site_symmetries.py b/pymatgen/symmetry/site_symmetries.py index 9568fe103b4..1b59ec0e60b 100644 --- a/pymatgen/symmetry/site_symmetries.py +++ b/pymatgen/symmetry/site_symmetries.py @@ -21,7 +21,7 @@ def get_site_symmetries(struct: Structure, precision: float = 0.1) -> list[list[ struct: Pymatgen structure precision (float): tolerance to find symmetry operations - Return: + Returns: list of lists of point operations for each atomic site """ point_ops: list[list[SymmOp]] = [] @@ -55,7 +55,7 @@ def get_shared_symmetry_operations(struct: Structure, pointops: list[list[SymmOp pointops: list of point group operations from get_site_symmetries method tol (float): tolerance to find symmetry operations - Return: + Returns: list of lists of shared point operations for each pair of atomic sites """ n_sites = len(struct) diff --git a/pymatgen/transformations/advanced_transformations.py b/pymatgen/transformations/advanced_transformations.py index e0325f87713..c20835c1c67 100644 --- a/pymatgen/transformations/advanced_transformations.py +++ b/pymatgen/transformations/advanced_transformations.py @@ -578,14 +578,16 @@ def __init__( site_constraint_name=None, site_constraints=None, ): - """:param order_parameter (float): any number from 0.0 to 1.0, - typically 0.5 (antiferromagnetic) or 1.0 (ferromagnetic) - :param species_constraint (list): str or list of strings - of Species symbols that the constraint should apply to - :param site_constraint_name (str): name of the site property - that the constraint should apply to, e.g. "coordination_no" - :param site_constraints (list): list of values of the site - property that the constraints should apply to + """ + Args: + order_parameter (float): any number from 0.0 to 1.0, + typically 0.5 (antiferromagnetic) or 1.0 (ferromagnetic) + species_constraints (list): str or list of strings + of Species symbols that the constraint should apply to + site_constraint_name (str): name of the site property + that the constraint should apply to, e.g. "coordination_no" + site_constraints (list): list of values of the site + property that the constraints should apply to. """ # validation if site_constraints and site_constraints != [None] and not site_constraint_name: @@ -632,22 +634,24 @@ class MagOrderingTransformation(AbstractTransformation): """ def __init__(self, mag_species_spin, order_parameter=0.5, energy_model=None, **kwargs): - """:param mag_species_spin: A mapping of elements/species to their - spin magnitudes, e.g. {"Fe3+": 5, "Mn3+": 4} - :param order_parameter (float or list): if float, a specifies a - global order parameter and can take values from 0.0 to 1.0 - (e.g. 0.5 for antiferromagnetic or 1.0 for ferromagnetic), if - list has to be a list of - pymatgen.transformations.advanced_transformations.MagOrderParameterConstraint - to specify more complicated orderings, see documentation for - MagOrderParameterConstraint more details on usage - :param energy_model: Energy model to rank the returned structures, - see :mod: `pymatgen.analysis.energy_models` for more information (note - that this is not necessarily a physical energy). By default, returned - structures use SymmetryModel() which ranks structures from most - symmetric to least. - :param kwargs: Additional kwargs that are passed to - EnumerateStructureTransformation such as min_cell_size etc. + """ + Args: + mag_species_spin: A mapping of elements/species to their + spin magnitudes, e.g. {"Fe3+": 5, "Mn3+": 4} + order_parameter (float or list): if float, a specifies a + global order parameter and can take values from 0.0 to 1.0 + (e.g. 0.5 for antiferromagnetic or 1.0 for ferromagnetic), if + list has to be a list of + pymatgen.transformations.advanced_transformations.MagOrderParameterConstraint + to specify more complicated orderings, see documentation for + MagOrderParameterConstraint more details on usage + energy_model: Energy model to rank the returned structures, + see :mod: `pymatgen.analysis.energy_models` for more information (note + that this is not necessarily a physical energy). By default, returned + structures use SymmetryModel() which ranks structures from most + symmetric to least. + kwargs: Additional kwargs that are passed to + EnumerateStructureTransformation such as min_cell_size etc. """ # checking for sensible order_parameter values if isinstance(order_parameter, float): @@ -710,8 +714,10 @@ def lcm(n1, n2): @staticmethod def _add_dummy_species(structure, order_parameters): - """:param structure: ordered Structure - :param order_parameters: list of MagOrderParameterConstraints + """ + Args: + structure: ordered Structure + order_parameters: list of MagOrderParameterConstraints. Returns: A structure decorated with disordered diff --git a/pymatgen/transformations/site_transformations.py b/pymatgen/transformations/site_transformations.py index bfb346508a5..29959391280 100644 --- a/pymatgen/transformations/site_transformations.py +++ b/pymatgen/transformations/site_transformations.py @@ -49,12 +49,12 @@ def __init__(self, species, coords, coords_are_cartesian=False, validate_proximi def apply_transformation(self, structure: Structure): """Apply the transformation. - Arg: + Args: structure (Structure): A structurally similar structure in regards to crystal and site positions. - Return: - Returns a copy of structure with sites inserted. + Returns: + A copy of structure with sites inserted. """ struct = structure.copy() for idx, sp in enumerate(self.species): @@ -99,12 +99,12 @@ def __init__(self, indices_species_map): def apply_transformation(self, structure: Structure): """Apply the transformation. - Arg: + Args: structure (Structure): A structurally similar structure in regards to crystal and site positions. - Return: - Returns a copy of structure with sites replaced. + Returns: + A copy of structure with sites replaced. """ struct = structure.copy() for i, sp in self.indices_species_map.items(): @@ -140,12 +140,12 @@ def __init__(self, indices_to_remove): def apply_transformation(self, structure: Structure): """Apply the transformation. - Arg: + Args: structure (Structure): A structurally similar structure in regards to crystal and site positions. - Return: - Returns a copy of structure with sites removed. + Returns: + A copy of structure with sites removed. """ struct = structure.copy() struct.remove_sites(self.indices_to_remove) @@ -187,12 +187,12 @@ def __init__(self, indices_to_move, translation_vector, vector_in_frac_coords=Tr def apply_transformation(self, structure: Structure): """Apply the transformation. - Arg: + Args: structure (Structure): A structurally similar structure in regards to crystal and site positions. - Return: - Returns a copy of structure with sites translated. + Returns: + A copy of structure with sites translated. """ struct = structure.copy() if self.translation_vector.shape == (len(self.indices_to_move), 3): @@ -509,12 +509,12 @@ def __init__(self, site_properties): def apply_transformation(self, structure: Structure): """Apply the transformation. - Arg: + Args: structure (Structure): A structurally similar structure in regards to crystal and site positions. - Return: - Returns a copy of structure with sites properties added. + Returns: + A copy of structure with sites properties added. """ new_struct = structure.copy() for prop in self.site_properties: diff --git a/pymatgen/transformations/standard_transformations.py b/pymatgen/transformations/standard_transformations.py index ae4bcfed96f..9945671fb71 100644 --- a/pymatgen/transformations/standard_transformations.py +++ b/pymatgen/transformations/standard_transformations.py @@ -967,7 +967,7 @@ def apply_transformation(self, structure): """Returns a copy of structure with lattice parameters and sites scaled to the same degree as the relaxed_structure. - Arg: + Args: structure (Structure): A structurally similar structure in regards to crystal and site positions. """ diff --git a/pymatgen/util/string.py b/pymatgen/util/string.py index 33e6cb9b9ff..a056a0244dc 100644 --- a/pymatgen/util/string.py +++ b/pymatgen/util/string.py @@ -162,26 +162,28 @@ def latexify(formula: str, bold: bool = False): return re.sub(r"([A-Za-z\(\)])([\d\.]+)", r"\1$_{\\mathbf{\2}}$" if bold else r"\1$_{\2}$", formula) -def htmlify(formula): +def htmlify(formula: str) -> str: """Generates a HTML formatted formula, e.g. Fe2O3 is transformed to Fe2O3. Note that Composition now has a to_html_string() method that may be used instead. - :param formula: + Args: + formula: The string to format. """ return re.sub(r"([A-Za-z\(\)])([\d\.]+)", r"\1\2", formula) -def unicodeify(formula): +def unicodeify(formula: str) -> str: """Generates a formula with unicode subscripts, e.g. Fe2O3 is transformed to Fe₂O₃. Does not support formulae with decimal points. Note that Composition now has a to_unicode_string() method that may be used instead. - :param formula: + Args: + formula: The string to format. """ if "." in formula: raise ValueError("No unicode character exists for subscript period.") diff --git a/pymatgen/vis/plotters.py b/pymatgen/vis/plotters.py index 43e4592693f..cae4d50e88c 100644 --- a/pymatgen/vis/plotters.py +++ b/pymatgen/vis/plotters.py @@ -13,7 +13,7 @@ class SpectrumPlotter: """ Class for plotting Spectrum objects and subclasses. Note that the interface is extremely flexible given that there are many different ways in which - people want to view spectra. The typical usage is:: + people want to view spectra. The typical usage is: # Initializes plotter with some optional args. Defaults are usually # fine, diff --git a/requirements-optional.txt b/requirements-optional.txt index a7269ecd2c8..b5a29956285 100644 --- a/requirements-optional.txt +++ b/requirements-optional.txt @@ -1,14 +1,14 @@ ase>=3.22.1 +BoltzTraP2>=22.3.2 chemview>=0.6 -netCDF4>=1.5.8 +f90nml>=1.4.3 fdint>=2.0.2 -phonopy==2.20.0 +galore>=0.7.0 h5py==3.9.0 -BoltzTraP2>=22.3.2 -f90nml>=1.4.3 # hiphive>=0.6 -seekpath>=2.0.1 +icet>=2.2 jarvis-tools>=2022.9.16 -galore>=0.7.0 matgl==1.0.0 -icet>=2.2 +netCDF4>=1.5.8 +phonopy==2.20.0 +seekpath>=2.0.1 diff --git a/setup.py b/setup.py index 673db0f3193..4e35d623ce0 100644 --- a/setup.py +++ b/setup.py @@ -60,6 +60,7 @@ "pytest-split", "pytest", "ruff", + "typing-extensions", ], "docs": [ "sphinx", diff --git a/tasks.py b/tasks.py index 0f5d23b88ba..7cf78730856 100644 --- a/tasks.py +++ b/tasks.py @@ -14,6 +14,7 @@ import re import subprocess import webbrowser +from typing import TYPE_CHECKING import requests from invoke import task @@ -21,13 +22,17 @@ from pymatgen.core import __version__ +if TYPE_CHECKING: + from invoke import Context + @task -def make_doc(ctx): +def make_doc(ctx: Context) -> None: """ Generate API documentation + run Sphinx. - :param ctx: + Args: + ctx (Context): The context. """ with cd("docs"): ctx.run("touch apidoc/index.rst", warn=True) @@ -70,11 +75,12 @@ def make_doc(ctx): @task -def publish(ctx): +def publish(ctx: Context) -> None: """ Upload release to Pypi using twine. - :param ctx: Context + Args: + ctx (Context): The context. """ ctx.run("rm dist/*.*", warn=True) ctx.run("python setup.py sdist bdist_wheel") @@ -82,7 +88,7 @@ def publish(ctx): @task -def set_ver(ctx, version): +def set_ver(ctx: Context, version: str): with open("setup.py") as file: contents = file.read() contents = re.sub(r"version=([^,]+),", f"version={version!r},", contents) @@ -92,11 +98,12 @@ def set_ver(ctx, version): @task -def release_github(ctx, version): +def release_github(ctx: Context, version: str) -> None: """ Release to Github using Github API. - :param ctx: + Args: + version (str): The version. """ with open("docs/CHANGES.md") as file: contents = file.read() @@ -120,11 +127,12 @@ def release_github(ctx, version): print(response.text) -def post_discourse(version): +def post_discourse(version: str) -> None: """ Post release announcement to http://discuss.matsci.org/c/pymatgen. - :param ctx: + Args: + version (str): The version. """ with open("CHANGES.rst") as file: contents = file.read() @@ -149,11 +157,16 @@ def post_discourse(version): @task -def update_changelog(ctx, version=None, dry_run=False): +def update_changelog(ctx: Context, version: str | None = None, dry_run: bool = False) -> None: """ Create a preliminary change log using the git logs. - :param ctx: + Args: + ctx (invoke.Context): The context object. + version (str, optional): The version to use for the change log. If not provided, it will + use the current date in the format 'YYYY.M.D'. Defaults to None. + dry_run (bool, optional): If True, the function will only print the changes without + updating the actual change log file. Defaults to False. """ version = version or f"{datetime.datetime.now():%Y.%-m.%-d}" output = subprocess.check_output(["git", "log", "--pretty=format:%s", f"v{__version__}..HEAD"]) @@ -191,12 +204,14 @@ def update_changelog(ctx, version=None, dry_run=False): @task -def release(ctx, version=None, nodoc=False): +def release(ctx: Context, version: str | None = None, nodoc: bool = False) -> None: """ Run full sequence for releasing pymatgen. - :param ctx: - :param nodoc: Whether to skip doc generation. + Args: + ctx (invoke.Context): The context object. + version (str, optional): The version to release. + nodoc (bool, optional): Whether to skip documentation generation. """ version = version or f"{datetime.datetime.now():%Y.%-m.%-d}" ctx.run("rm -r dist build pymatgen.egg-info", warn=True) @@ -216,7 +231,7 @@ def release(ctx, version=None, nodoc=False): @task -def open_doc(ctx): +def open_doc(ctx: Context) -> None: """ Open local documentation in web browser. """ @@ -225,6 +240,12 @@ def open_doc(ctx): @task -def lint(ctx): +def lint(ctx: Context) -> None: + """ + Run linting tools. + + Args: + ctx (invoke.Context): The context object. + """ for cmd in ["ruff", "mypy", "ruff format"]: ctx.run(f"{cmd} pymatgen") diff --git a/tests/analysis/chemenv/utils/test_chemenv_config.py b/tests/analysis/chemenv/utils/test_chemenv_config.py index d913c17e73d..d1c03bca235 100644 --- a/tests/analysis/chemenv/utils/test_chemenv_config.py +++ b/tests/analysis/chemenv/utils/test_chemenv_config.py @@ -23,10 +23,10 @@ def test_chemenv_config(self): config.package_options_description() == "Package options :\n" " - Maximum distance factor : 1.8000\n" ' - Default strategy is "SimplestChemenvStrategy" :\n' - " Simplest ChemenvStrategy using fixed angle and distance parameters \n" - " for the definition of neighbors in the Voronoi approach. \n" - " The coordination environment is then given as the one with the \n" - " lowest continuous symmetry measure.\n" + "Simplest ChemenvStrategy using fixed angle and distance parameters \n" + "for the definition of neighbors in the Voronoi approach. \n" + "The coordination environment is then given as the one with the \n" + "lowest continuous symmetry measure.\n" " with options :\n" " - distance_cutoff : 1.4\n" " - angle_cutoff : 0.3\n"