diff --git a/dev_scripts/potcar_scrambler.py b/dev_scripts/potcar_scrambler.py index 37c586c670d..358cdc6d799 100644 --- a/dev_scripts/potcar_scrambler.py +++ b/dev_scripts/potcar_scrambler.py @@ -95,10 +95,10 @@ def to_file(self, filename: str): with zopen(filename, "wt") as f: f.write(self.scrambled_potcars_str) - @staticmethod - def from_file(input_filename: str, output_filename: str | None = None): + @classmethod + def from_file(cls, input_filename: str, output_filename: str | None = None): psp = Potcar.from_file(input_filename) - psp_scrambled = PotcarScrambler(psp) + psp_scrambled = cls(psp) if output_filename: psp_scrambled.to_file(output_filename) return psp_scrambled diff --git a/pymatgen/analysis/reaction_calculator.py b/pymatgen/analysis/reaction_calculator.py index 2cd65289e49..885720c75a9 100644 --- a/pymatgen/analysis/reaction_calculator.py +++ b/pymatgen/analysis/reaction_calculator.py @@ -249,8 +249,8 @@ def from_dict(cls, d): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(rxn_str): + @classmethod + def from_str(cls, rxn_str): """ Generates a balanced reaction from a string. The reaction must already be balanced. diff --git a/pymatgen/core/periodic_table.py b/pymatgen/core/periodic_table.py index b58e129b49e..66e825a871b 100644 --- a/pymatgen/core/periodic_table.py +++ b/pymatgen/core/periodic_table.py @@ -1011,8 +1011,8 @@ def from_string(cls, *args, **kwargs): """Use from_str instead.""" return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(species_string: str) -> Species: + @classmethod + def from_str(cls, species_string: str) -> Species: """Returns a Species from a string representation. Args: @@ -1051,10 +1051,10 @@ def from_str(species_string: str) -> Species: # but we need either an oxidation state or a property if oxi is None and properties == {}: - raise ValueError("Invalid Species String") + raise ValueError("Invalid species string") - return Species(sym, 0 if oxi is None else oxi, **properties) - raise ValueError("Invalid Species String") + return cls(sym, 0 if oxi is None else oxi, **properties) + raise ValueError("Invalid species string") def __repr__(self): return f"Species {self}" @@ -1294,8 +1294,8 @@ def symbol(self) -> str: def __deepcopy__(self, memo): return DummySpecies(self.symbol, self._oxi_state) - @staticmethod - def from_str(species_string: str) -> DummySpecies: + @classmethod + def from_str(cls, species_string: str) -> DummySpecies: """Returns a Dummy from a string representation. Args: @@ -1320,7 +1320,7 @@ def from_str(species_string: str) -> DummySpecies: if m.group(4): # has Spin property tokens = m.group(4).split("=") properties = {tokens[0]: float(tokens[1])} - return DummySpecies(sym, oxi, **properties) + return cls(sym, oxi, **properties) raise ValueError("Invalid DummySpecies String") def as_dict(self) -> dict: diff --git a/pymatgen/electronic_structure/boltztrap.py b/pymatgen/electronic_structure/boltztrap.py index 3885d33ae06..7934e005b8d 100644 --- a/pymatgen/electronic_structure/boltztrap.py +++ b/pymatgen/electronic_structure/boltztrap.py @@ -1924,8 +1924,8 @@ def parse_cond_and_hall(path_dir, doping_levels=None): carrier_conc, ) - @staticmethod - def from_files(path_dir, dos_spin=1): + @classmethod + def from_files(cls, path_dir, dos_spin=1): """Get a BoltztrapAnalyzer object from a set of files. Args: @@ -1935,29 +1935,29 @@ def from_files(path_dir, dos_spin=1): Returns: a BoltztrapAnalyzer object """ - run_type, warning, efermi, gap, doping_levels = BoltztrapAnalyzer.parse_outputtrans(path_dir) + run_type, warning, efermi, gap, doping_levels = cls.parse_outputtrans(path_dir) - vol = BoltztrapAnalyzer.parse_struct(path_dir) + vol = cls.parse_struct(path_dir) - intrans = BoltztrapAnalyzer.parse_intrans(path_dir) + intrans = cls.parse_intrans(path_dir) if run_type == "BOLTZ": - dos, pdos = BoltztrapAnalyzer.parse_transdos(path_dir, efermi, dos_spin=dos_spin, trim_dos=False) + dos, pdos = cls.parse_transdos(path_dir, efermi, dos_spin=dos_spin, trim_dos=False) - *cond_and_hall, carrier_conc = BoltztrapAnalyzer.parse_cond_and_hall(path_dir, doping_levels) + *cond_and_hall, carrier_conc = cls.parse_cond_and_hall(path_dir, doping_levels) - return BoltztrapAnalyzer(gap, *cond_and_hall, intrans, dos, pdos, carrier_conc, vol, warning) + return cls(gap, *cond_and_hall, intrans, dos, pdos, carrier_conc, vol, warning) if run_type == "DOS": trim = intrans["dos_type"] == "HISTO" - dos, pdos = BoltztrapAnalyzer.parse_transdos(path_dir, efermi, dos_spin=dos_spin, trim_dos=trim) + dos, pdos = cls.parse_transdos(path_dir, efermi, dos_spin=dos_spin, trim_dos=trim) - return BoltztrapAnalyzer(gap=gap, dos=dos, dos_partial=pdos, warning=warning, vol=vol) + return cls(gap=gap, dos=dos, dos_partial=pdos, warning=warning, vol=vol) if run_type == "BANDS": bz_kpoints = np.loadtxt(f"{path_dir}/boltztrap_band.dat")[:, -3:] bz_bands = np.loadtxt(f"{path_dir}/boltztrap_band.dat")[:, 1:-6] - return BoltztrapAnalyzer(bz_bands=bz_bands, bz_kpoints=bz_kpoints, warning=warning, vol=vol) + return cls(bz_bands=bz_bands, bz_kpoints=bz_kpoints, warning=warning, vol=vol) if run_type == "FERMI": if os.path.exists(f"{path_dir}/boltztrap_BZ.cube"): @@ -1966,7 +1966,7 @@ def from_files(path_dir, dos_spin=1): fs_data = read_cube_file(f"{path_dir}/fort.30") else: raise BoltztrapError("No data file found for fermi surface") - return BoltztrapAnalyzer(fermi_surface_data=fs_data) + return cls(fermi_surface_data=fs_data) raise ValueError(f"{run_type=} not recognized!") diff --git a/pymatgen/io/adf.py b/pymatgen/io/adf.py index d87c35d63bd..e0938ce4505 100644 --- a/pymatgen/io/adf.py +++ b/pymatgen/io/adf.py @@ -44,25 +44,6 @@ def is_numeric(s) -> bool: return True -def iterlines(s: str) -> Generator[str, None, None]: - r"""A generator form of s.split('\n') for reducing memory overhead. - - Args: - s (str): A multi-line string. - - Yields: - str: line - """ - prevnl = -1 - while True: - nextnl = s.find("\n", prevnl + 1) - if nextnl < 0: - yield s[(prevnl + 1) :] - break - yield s[(prevnl + 1) : nextnl] - prevnl = nextnl - - class AdfInputError(Exception): """The default error class for ADF.""" @@ -362,8 +343,8 @@ def from_dict(cls, d): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(string): + @classmethod + def from_str(cls, string: str) -> AdfKey: """ Construct an AdfKey object from the string. @@ -395,18 +376,36 @@ def is_float(s) -> bool: el = string.split() if len(el) > 1: options = [s.split("=") for s in el[1:]] if string.find("=") != -1 else el[1:] - for i, op in enumerate(options): + for idx, op in enumerate(options): # type: ignore[var-annotated, arg-type] if isinstance(op, list) and is_numeric(op[1]): op[1] = float(op[1]) if is_float(op[1]) else int(op[1]) elif is_numeric(op): - options[i] = float(op) if is_float(op) else int(op) + options[idx] = float(op) if is_float(op) else int(op) # type: ignore[index] else: options = None - return AdfKey(el[0], options) + return cls(el[0], options) if string.find("subend") != -1: raise ValueError("Nested subkeys are not supported!") + def iterlines(s: str) -> Generator[str, None, None]: + r"""A generator form of s.split('\n') for reducing memory overhead. + + Args: + s (str): A multi-line string. + + Yields: + str: line + """ + prev_nl = -1 + while True: + next_nl = s.find("\n", prev_nl + 1) + if next_nl < 0: + yield s[(prev_nl + 1) :] + break + yield s[(prev_nl + 1) : next_nl] + prev_nl = next_nl + key = None for line in iterlines(string): if line == "": @@ -414,15 +413,15 @@ def is_float(s) -> bool: el = line.strip().split() if len(el) == 0: continue - if el[0].upper() in AdfKey.block_keys: + if el[0].upper() in cls.block_keys: if key is None: - key = AdfKey.from_str(line) + key = cls.from_str(line) else: return key elif el[0].upper() == "END": - return key + return key # type: ignore[return-value] elif key is not None: - key.add_subkey(AdfKey.from_str(line)) + key.add_subkey(cls.from_str(line)) raise Exception("IncompleteKey: 'END' is missing!") diff --git a/pymatgen/io/babel.py b/pymatgen/io/babel.py index 423653e62dd..d58369ba47b 100644 --- a/pymatgen/io/babel.py +++ b/pymatgen/io/babel.py @@ -305,8 +305,8 @@ def write_file(self, filename, file_format="xyz"): mol = pybel.Molecule(self._ob_mol) return mol.write(file_format, filename, overwrite=True) - @staticmethod - def from_file(filename, file_format="xyz", return_all_molecules=False): + @classmethod + def from_file(cls, filename, file_format="xyz", return_all_molecules=False): """ Uses OpenBabel to read a molecule from a file in all supported formats. @@ -322,9 +322,9 @@ def from_file(filename, file_format="xyz", return_all_molecules=False): """ mols = pybel.readfile(str(file_format), str(filename)) if return_all_molecules: - return [BabelMolAdaptor(mol.OBMol) for mol in mols] + return [cls(mol.OBMol) for mol in mols] - return BabelMolAdaptor(next(mols).OBMol) + return cls(next(mols).OBMol) @staticmethod def from_molecule_graph(mol): @@ -345,8 +345,8 @@ def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) @needs_openbabel - @staticmethod - def from_str(string_data, file_format="xyz"): + @classmethod + def from_str(cls, string_data, file_format="xyz"): """ Uses OpenBabel to read a molecule from a string in all supported formats. @@ -359,4 +359,4 @@ def from_str(string_data, file_format="xyz"): BabelMolAdaptor object """ mols = pybel.readstring(str(file_format), str(string_data)) - return BabelMolAdaptor(mols.OBMol) + return cls(mols.OBMol) diff --git a/pymatgen/io/cif.py b/pymatgen/io/cif.py index 7aa8bed4254..40a903664dd 100644 --- a/pymatgen/io/cif.py +++ b/pymatgen/io/cif.py @@ -366,8 +366,8 @@ def is_magcif_incommensurate() -> bool: def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(cif_string: str, **kwargs) -> CifParser: + @classmethod + def from_str(cls, cif_string: str, **kwargs) -> CifParser: """ Creates a CifParser from a string. @@ -379,7 +379,7 @@ def from_str(cif_string: str, **kwargs) -> CifParser: CifParser """ stream = StringIO(cif_string) - return CifParser(stream, **kwargs) + return cls(stream, **kwargs) def _sanitize_data(self, data): """ diff --git a/pymatgen/io/cp2k/inputs.py b/pymatgen/io/cp2k/inputs.py index 0471e53b1ab..60da9c263b5 100644 --- a/pymatgen/io/cp2k/inputs.py +++ b/pymatgen/io/cp2k/inputs.py @@ -160,12 +160,12 @@ def from_dict(cls, d): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(s): + @classmethod + def from_str(cls, s): """ Initialize from a string. - Keywords must be labeled with strings. If the postprocessor finds + Keywords must be labeled with strings. If the post-processor finds that the keywords is a number, then None is return (used by the file reader). @@ -183,7 +183,7 @@ def from_str(s): args = s.split() args = list(map(postprocessor if args[0].upper() != "ELEMENT" else str, args)) args[0] = str(args[0]) - return Keyword(*args, units=units[0], description=description) + return cls(*args, units=units[0], description=description) def verbosity(self, v): """Change the printing of this keyword's description.""" @@ -730,26 +730,26 @@ def _from_dict(cls, d): .subsections, ) - @staticmethod - def from_file(file: str): + @classmethod + def from_file(cls, file: str): """Initialize from a file.""" with zopen(file, "rt") as f: txt = preprocessor(f.read(), os.path.dirname(f.name)) - return Cp2kInput.from_str(txt) + return cls.from_str(txt) @classmethod @np.deprecate(message="Use from_str instead") def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(s: str): + @classmethod + def from_str(cls, s: str): """Initialize from a string.""" lines = s.splitlines() lines = [line.replace("\t", "") for line in lines] lines = [line.strip() for line in lines] lines = [line for line in lines if line] - return Cp2kInput.from_lines(lines) + return cls.from_lines(lines) @classmethod def from_lines(cls, lines: list | tuple): diff --git a/pymatgen/io/cssr.py b/pymatgen/io/cssr.py index 613d71ef6ef..6b745271eab 100644 --- a/pymatgen/io/cssr.py +++ b/pymatgen/io/cssr.py @@ -55,8 +55,8 @@ def write_file(self, filename): with zopen(filename, "wt") as f: f.write(str(self) + "\n") - @staticmethod - def from_str(string): + @classmethod + def from_str(cls, string): """ Reads a string representation to a Cssr object. @@ -79,10 +79,10 @@ def from_str(string): if m: sp.append(m.group(1)) coords.append([float(m.group(i)) for i in range(2, 5)]) - return Cssr(Structure(latt, sp, coords)) + return cls(Structure(latt, sp, coords)) - @staticmethod - def from_file(filename): + @classmethod + def from_file(cls, filename): """ Reads a CSSR file to a Cssr object. @@ -93,4 +93,4 @@ def from_file(filename): Cssr object. """ with zopen(filename, "rt") as f: - return Cssr.from_str(f.read()) + return cls.from_str(f.read()) diff --git a/pymatgen/io/exciting/inputs.py b/pymatgen/io/exciting/inputs.py index e02e3197e37..297bd7ba1b4 100644 --- a/pymatgen/io/exciting/inputs.py +++ b/pymatgen/io/exciting/inputs.py @@ -70,11 +70,11 @@ def lockxyz(self, lockxyz): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(data): + @classmethod + def from_str(cls, data): """Reads the exciting input from a string.""" root = ET.fromstring(data) - speciesnode = root.find("structure").iter("species") + species_node = root.find("structure").iter("species") elements = [] positions = [] vectors = [] @@ -82,7 +82,7 @@ def from_str(data): # get title title_in = str(root.find("title").text) # Read elements and coordinates - for nodes in speciesnode: + for nodes in species_node: symbol = nodes.get("speciesfile").split(".")[0] if len(symbol.split("_")) == 2: symbol = symbol.split("_")[0] @@ -137,10 +137,10 @@ def from_str(data): lattice_in = Lattice(vectors) structure_in = Structure(lattice_in, elements, positions, coords_are_cartesian=cartesian) - return ExcitingInput(structure_in, title_in, lockxyz) + return cls(structure_in, title_in, lockxyz) - @staticmethod - def from_file(filename): + @classmethod + def from_file(cls, filename): """ :param filename: Filename @@ -149,7 +149,7 @@ def from_file(filename): """ with zopen(filename, "rt") as f: data = f.read().replace("\n", "") - return ExcitingInput.from_str(data) + return cls.from_str(data) def write_etree(self, celltype, cartesian=False, bandstr=False, symprec: float = 0.4, angle_tolerance=5, **kwargs): """ diff --git a/pymatgen/io/feff/inputs.py b/pymatgen/io/feff/inputs.py index d42a766663a..d73a9341f91 100644 --- a/pymatgen/io/feff/inputs.py +++ b/pymatgen/io/feff/inputs.py @@ -228,11 +228,11 @@ def formula(self): """Formula of structure.""" return self.struct.composition.formula - @staticmethod - def from_file(filename): + @classmethod + def from_file(cls, filename): """Returns Header object from file.""" - hs = Header.header_string_from_file(filename) - return Header.from_str(hs) + hs = cls.header_string_from_file(filename) + return cls.from_str(hs) @staticmethod def header_string_from_file(filename="feff.inp"): @@ -283,8 +283,8 @@ def header_string_from_file(filename="feff.inp"): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(header_str): + @classmethod + def from_str(cls, header_str): """ Reads Header string and returns Header object if header was generated by pymatgen. @@ -336,7 +336,7 @@ def from_str(header_str): struct = Structure(lattice, atomic_symbols, coords) - return Header(struct, source, comment2) + return cls(struct, source, comment2) raise ValueError("Header not generated by pymatgen, cannot return header object") @@ -675,8 +675,8 @@ def write_file(self, filename="PARAMETERS"): with zopen(filename, "wt") as f: f.write(f"{self}\n") - @staticmethod - def from_file(filename="feff.inp"): + @classmethod + def from_file(cls, filename="feff.inp"): """ Creates a Feff_tag dictionary from a PARAMETER or feff.inp file. @@ -725,7 +725,7 @@ def from_file(filename="feff.inp"): eels_dict[k] = str(v) params[str(eels_params[0].split()[0])] = eels_dict - return Tags(params) + return cls(params) @staticmethod def proc_val(key, val): diff --git a/pymatgen/io/feff/outputs.py b/pymatgen/io/feff/outputs.py index 1116d04454d..4cd0ffd5c27 100644 --- a/pymatgen/io/feff/outputs.py +++ b/pymatgen/io/feff/outputs.py @@ -42,8 +42,8 @@ def __init__(self, complete_dos, charge_transfer): self.complete_dos = complete_dos self.charge_transfer = charge_transfer - @staticmethod - def from_file(feff_inp_file="feff.inp", ldos_file="ldos"): + @classmethod + def from_file(cls, feff_inp_file="feff.inp", ldos_file="ldos"): """ Creates LDos object from raw Feff ldos files by by assuming they are numbered consecutively, i.e. ldos01.dat @@ -144,7 +144,7 @@ def from_file(feff_inp_file="feff.inp", ldos_file="ldos"): dos = Dos(efermi, dos_energies, t_dos) complete_dos = CompleteDos(structure, dos, pdoss) charge_transfer = LDos.charge_transfer_from_file(feff_inp_file, ldos_file) - return LDos(complete_dos, charge_transfer) + return cls(complete_dos, charge_transfer) @staticmethod def charge_transfer_from_file(feff_inp_file, ldos_file): @@ -287,8 +287,8 @@ def __init__(self, header, parameters, absorbing_atom, data): self.absorbing_atom = absorbing_atom self.data = np.array(data) - @staticmethod - def from_file(xmu_dat_file="xmu.dat", feff_inp_file="feff.inp"): + @classmethod + def from_file(cls, xmu_dat_file="xmu.dat", feff_inp_file="feff.inp"): """ Get Xmu from file. @@ -307,7 +307,7 @@ def from_file(xmu_dat_file="xmu.dat", feff_inp_file="feff.inp"): # site index (Note: in feff it starts from 1) # else case is species symbol absorbing_atom = parameters["TARGET"] if "RECIPROCAL" in parameters else pots.splitlines()[3].split()[2] - return Xmu(header, parameters, absorbing_atom, data) + return cls(header, parameters, absorbing_atom, data) @property def energies(self): @@ -412,8 +412,8 @@ def fine_structure(self): """Returns: Fine structure of EELS.""" return self.data[:, 3] - @staticmethod - def from_file(eels_dat_file="eels.dat"): + @classmethod + def from_file(cls, eels_dat_file="eels.dat"): """ Parse eels spectrum. @@ -424,7 +424,7 @@ def from_file(eels_dat_file="eels.dat"): Eels """ data = np.loadtxt(eels_dat_file) - return Eels(data) + return cls(data) def as_dict(self): """Returns dict representations of Xmu object.""" diff --git a/pymatgen/io/gaussian.py b/pymatgen/io/gaussian.py index 6da24dac078..e9b5f8b889b 100644 --- a/pymatgen/io/gaussian.py +++ b/pymatgen/io/gaussian.py @@ -280,8 +280,8 @@ def _parse_species(sp_str): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(contents): + @classmethod + def from_str(cls, contents): """ Creates GaussianInput from a string. @@ -328,7 +328,7 @@ def from_str(contents): spaces = 0 input_paras = {} ind += 1 - if GaussianInput._xyz_patt.match(lines[route_index + ind]): + if cls._xyz_patt.match(lines[route_index + ind]): spaces += 1 for i in range(route_index + ind, len(lines)): if lines[i].strip() == "": @@ -339,10 +339,10 @@ def from_str(contents): input_paras[d[0]] = d[1] else: coord_lines.append(lines[i].strip()) - mol = GaussianInput._parse_coords(coord_lines) + mol = cls._parse_coords(coord_lines) mol.set_charge_and_spin(charge, spin_mult) - return GaussianInput( + return cls( mol, charge=charge, spin_multiplicity=spin_mult, @@ -355,8 +355,8 @@ def from_str(contents): dieze_tag=dieze_tag, ) - @staticmethod - def from_file(filename): + @classmethod + def from_file(cls, filename): """ Creates GaussianInput from a file. @@ -367,7 +367,7 @@ def from_file(filename): GaussianInput object """ with zopen(filename, "r") as f: - return GaussianInput.from_str(f.read()) + return cls.from_str(f.read()) def get_zmatrix(self): """Returns a z-matrix representation of the molecule.""" diff --git a/pymatgen/io/pwscf.py b/pymatgen/io/pwscf.py index ef8a63a90b3..cea2a5b48d8 100644 --- a/pymatgen/io/pwscf.py +++ b/pymatgen/io/pwscf.py @@ -220,8 +220,8 @@ def write_file(self, filename): with open(filename, "w") as f: f.write(str(self)) - @staticmethod - def from_file(filename): + @classmethod + def from_file(cls, filename): """ Reads an PWInput object from a file. @@ -232,15 +232,15 @@ def from_file(filename): PWInput object """ with zopen(filename, "rt") as f: - return PWInput.from_str(f.read()) + return cls.from_str(f.read()) @classmethod @np.deprecate(message="Use from_str instead") def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(string): + @classmethod + def from_str(cls, string): """ Reads an PWInput object from a string. @@ -343,7 +343,7 @@ def input_mode(line): coords_are_cartesian=coords_are_cartesian, site_properties=site_properties, ) - return PWInput( + return cls( structure=structure, control=sections["control"], pseudo=pseudo, diff --git a/pymatgen/io/qchem/inputs.py b/pymatgen/io/qchem/inputs.py index 8bea7d319e1..2928d4fc1a8 100644 --- a/pymatgen/io/qchem/inputs.py +++ b/pymatgen/io/qchem/inputs.py @@ -347,8 +347,8 @@ def write_multi_job_file(job_list: list[QCInput], filename: str): with zopen(filename, "wt") as f: f.write(QCInput.multi_job_string(job_list)) - @staticmethod - def from_file(filename: str | Path) -> QCInput: + @classmethod + def from_file(cls, filename: str | Path) -> QCInput: """ Create QcInput from file. @@ -359,7 +359,7 @@ def from_file(filename: str | Path) -> QCInput: QcInput """ with zopen(filename, "rt") as f: - return QCInput.from_str(f.read()) + return cls.from_str(f.read()) @classmethod def from_multi_jobs_file(cls, filename: str) -> list[QCInput]: diff --git a/pymatgen/io/vasp/inputs.py b/pymatgen/io/vasp/inputs.py index ae120378821..710dfd489b1 100644 --- a/pymatgen/io/vasp/inputs.py +++ b/pymatgen/io/vasp/inputs.py @@ -224,8 +224,8 @@ def __setattr__(self, name, value): value = value.tolist() super().__setattr__(name, value) - @staticmethod - def from_file(filename, check_for_potcar=True, read_velocities=True, **kwargs) -> Poscar: + @classmethod + def from_file(cls, filename, check_for_potcar=True, read_velocities=True, **kwargs) -> Poscar: """ Reads a Poscar from a file. @@ -273,15 +273,15 @@ def from_file(filename, check_for_potcar=True, read_velocities=True, **kwargs) - except Exception: names = None with zopen(filename, "rt") as f: - return Poscar.from_str(f.read(), names, read_velocities=read_velocities) + return cls.from_str(f.read(), names, read_velocities=read_velocities) @classmethod @deprecated(message="Use from_str instead") def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(data, default_names=None, read_velocities=True): + @classmethod + def from_str(cls, data, default_names=None, read_velocities=True): """ Reads a Poscar from a string. @@ -477,7 +477,7 @@ def from_str(data, default_names=None, read_velocities=True): else: velocities = predictor_corrector = predictor_corrector_preamble = lattice_velocities = None - return Poscar( + return cls( struct, comment, selective_dynamics, @@ -783,8 +783,8 @@ def write_file(self, filename: PathLike): with zopen(filename, "wt") as f: f.write(str(self)) - @staticmethod - def from_file(filename: PathLike) -> Incar: + @classmethod + def from_file(cls, filename: PathLike) -> Incar: """Reads an Incar object from a file. Args: @@ -794,15 +794,15 @@ def from_file(filename: PathLike) -> Incar: Incar object """ with zopen(filename, "rt") as f: - return Incar.from_str(f.read()) + return cls.from_str(f.read()) @classmethod @np.deprecate(message="Use from_str instead") def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(string: str) -> Incar: + @classmethod + def from_str(cls, string: str) -> Incar: """Reads an Incar object from a string. Args: @@ -821,7 +821,7 @@ def from_str(string: str) -> Incar: val = m.group(2).strip() val = Incar.proc_val(key, val) params[key] = val - return Incar(params) + return cls(params) @staticmethod def proc_val(key: str, val: Any): @@ -1004,8 +1004,8 @@ def __str__(self): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(mode: str) -> KpointsSupportedModes: + @classmethod + def from_str(cls, mode: str) -> KpointsSupportedModes: """ :param s: String @@ -1348,8 +1348,8 @@ def automatic_linemode(divisions, ibz): num_kpts=int(divisions), ) - @staticmethod - def from_file(filename): + @classmethod + def from_file(cls, filename): """ Reads a Kpoints object from a KPOINTS file. @@ -1360,15 +1360,15 @@ def from_file(filename): Kpoints object """ with zopen(filename, "rt") as f: - return Kpoints.from_str(f.read()) + return cls.from_str(f.read()) @classmethod @np.deprecate(message="Use from_str instead") def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(string): + @classmethod + def from_str(cls, string): """ Reads a Kpoints object from a KPOINTS string. @@ -1386,7 +1386,7 @@ def from_str(string): # Fully automatic KPOINTS if style == "a": - return Kpoints.automatic(int(lines[3].split()[0].strip())) + return cls.automatic(int(lines[3].split()[0].strip())) coord_pattern = re.compile(r"^\s*([\d+.\-Ee]+)\s+([\d+.\-Ee]+)\s+([\d+.\-Ee]+)") @@ -1399,15 +1399,11 @@ def from_str(string): kpts_shift = [float(i) for i in lines[4].split()] except ValueError: pass - return ( - Kpoints.gamma_automatic(kpts, kpts_shift) - if style == "g" - else Kpoints.monkhorst_automatic(kpts, kpts_shift) - ) + return cls.gamma_automatic(kpts, kpts_shift) if style == "g" else cls.monkhorst_automatic(kpts, kpts_shift) # Automatic kpoints with basis if num_kpts <= 0: - style = Kpoints.supported_modes.Cartesian if style in "ck" else Kpoints.supported_modes.Reciprocal + style = cls.supported_modes.Cartesian if style in "ck" else cls.supported_modes.Reciprocal kpts = [[float(j) for j in lines[i].split()] for i in range(3, 6)] kpts_shift = [float(i) for i in lines[6].split()] return Kpoints( @@ -1421,7 +1417,7 @@ def from_str(string): # Line-mode KPOINTS, usually used with band structures if style == "l": coord_type = "Cartesian" if lines[3].lower()[0] in "ck" else "Reciprocal" - style = Kpoints.supported_modes.Line_mode + style = cls.supported_modes.Line_mode kpts = [] labels = [] patt = re.compile(r"([e0-9.\-]+)\s+([e0-9.\-]+)\s+([e0-9.\-]+)\s*!*\s*(.*)") @@ -1441,7 +1437,7 @@ def from_str(string): ) # Assume explicit KPOINTS if all else fails. - style = Kpoints.supported_modes.Cartesian if style in "ck" else Kpoints.supported_modes.Reciprocal + style = cls.supported_modes.Cartesian if style in "ck" else cls.supported_modes.Reciprocal kpts = [] kpts_weights = [] labels = [] @@ -1470,10 +1466,10 @@ def from_str(string): except IndexError: pass - return Kpoints( + return cls( comment=comment, num_kpts=num_kpts, - style=Kpoints.supported_modes[str(style)], + style=cls.supported_modes[str(style)], kpts=kpts, kpts_weights=kpts_weights, tet_number=tet_number, @@ -1819,8 +1815,8 @@ def write_file(self, filename: str) -> None: with zopen(filename, "wt") as file: file.write(str(self)) - @staticmethod - def from_file(filename: str) -> PotcarSingle: + @classmethod + def from_file(cls, filename: str) -> PotcarSingle: """ Reads PotcarSingle from file. @@ -1834,16 +1830,16 @@ def from_file(filename: str) -> PotcarSingle: try: with zopen(filename, "rt") as file: - return PotcarSingle(file.read(), symbol=symbol or None) + return cls(file.read(), symbol=symbol or None) except UnicodeDecodeError: warnings.warn("POTCAR contains invalid unicode errors. We will attempt to read it by ignoring errors.") import codecs with codecs.open(filename, "r", encoding="utf-8", errors="ignore") as file: - return PotcarSingle(file.read(), symbol=symbol or None) + return cls(file.read(), symbol=symbol or None) - @staticmethod - def from_symbol_and_functional(symbol: str, functional: str | None = None): + @classmethod + def from_symbol_and_functional(cls, symbol: str, functional: str | None = None): """ Makes a PotcarSingle from a symbol and functional. @@ -1856,7 +1852,7 @@ def from_symbol_and_functional(symbol: str, functional: str | None = None): """ functional = functional or SETTINGS.get("PMG_DEFAULT_FUNCTIONAL", "PBE") assert isinstance(functional, str) # mypy type narrowing - funcdir = PotcarSingle.functional_dir[functional] + funcdir = cls.functional_dir[functional] PMG_VASP_PSP_DIR = SETTINGS.get("PMG_VASP_PSP_DIR") if PMG_VASP_PSP_DIR is None: raise ValueError( @@ -1870,7 +1866,7 @@ def from_symbol_and_functional(symbol: str, functional: str | None = None): path = os.path.expanduser(path) path = zpath(path) if os.path.isfile(path): - return PotcarSingle.from_file(path) + return cls.from_file(path) raise OSError( f"You do not have the right POTCAR with {functional=} and {symbol=} " f"in your {PMG_VASP_PSP_DIR=}. Paths tried: {paths_to_try}" @@ -2430,8 +2426,8 @@ def from_dict(cls, d): """ return Potcar(symbols=d["symbols"], functional=d["functional"]) - @staticmethod - def from_file(filename: str): + @classmethod + def from_file(cls, filename: str): """ Reads Potcar from file. @@ -2442,7 +2438,7 @@ def from_file(filename: str): """ with zopen(filename, "rt") as f: fdata = f.read() - potcar = Potcar() + potcar = cls() functionals = [] for psingle_str in fdata.split("End of Dataset"): diff --git a/pymatgen/io/vasp/outputs.py b/pymatgen/io/vasp/outputs.py index 67b7203d7df..7c537232e0e 100644 --- a/pymatgen/io/vasp/outputs.py +++ b/pymatgen/io/vasp/outputs.py @@ -3463,8 +3463,8 @@ def __init__(self, poscar, data, data_aug=None): super().__init__(struct, data, data_aug=data_aug) self._distance_matrix = {} - @staticmethod - def from_file(filename: str): + @classmethod + def from_file(cls, filename: str): """Read a CHGCAR file. Args: @@ -3474,7 +3474,7 @@ def from_file(filename: str): Chgcar """ poscar, data, data_aug = VolumetricData.parse_file(filename) - return Chgcar(poscar, data, data_aug=data_aug) + return cls(poscar, data, data_aug=data_aug) @property def net_magnetization(self): diff --git a/pymatgen/io/wannier90.py b/pymatgen/io/wannier90.py index 257e24c2a9b..48ad3b67f46 100644 --- a/pymatgen/io/wannier90.py +++ b/pymatgen/io/wannier90.py @@ -88,8 +88,8 @@ def data(self, value: np.ndarray) -> None: self.nbnd = self.data.shape[0] self.ng = self.data.shape[-3:] - @staticmethod - def from_file(filename: str) -> object: + @classmethod + def from_file(cls, filename: str) -> object: """ Reads the UNK data from file. @@ -122,8 +122,8 @@ def from_file(filename: str) -> object: temp_data = np.empty((nbnd, 2, *ng), dtype=np.complex128) temp_data[:, 0, :, :, :] = data[::2, :, :, :] temp_data[:, 1, :, :, :] = data[1::2, :, :, :] - return Unk(ik, temp_data) - return Unk(ik, data) + return cls(ik, temp_data) + return cls(ik, data) def write_file(self, filename: str) -> None: """ diff --git a/pymatgen/io/xr.py b/pymatgen/io/xr.py index 50c07115281..3e0402ff872 100644 --- a/pymatgen/io/xr.py +++ b/pymatgen/io/xr.py @@ -73,8 +73,8 @@ def write_file(self, filename): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(string, use_cores=True, thresh=1.0e-4): + @classmethod + def from_str(cls, string, use_cores=True, thresh=1.0e-4): """ Creates an Xr object from a string representation. @@ -138,10 +138,10 @@ def from_str(string, use_cores=True, thresh=1.0e-4): else: sp.append(tmp_sp) coords.append([float(m.group(i)) for i in range(2, 5)]) - return Xr(Structure(lat, sp, coords, coords_are_cartesian=True)) + return cls(Structure(lat, sp, coords, coords_are_cartesian=True)) - @staticmethod - def from_file(filename, use_cores=True, thresh=1.0e-4): + @classmethod + def from_file(cls, filename, use_cores=True, thresh=1.0e-4): """ Reads an xr-formatted file to create an Xr object. @@ -159,4 +159,4 @@ def from_file(filename, use_cores=True, thresh=1.0e-4): file. """ with zopen(filename, "rt") as f: - return Xr.from_str(f.read(), use_cores=use_cores, thresh=thresh) + return cls.from_str(f.read(), use_cores=use_cores, thresh=thresh) diff --git a/pymatgen/io/xyz.py b/pymatgen/io/xyz.py index 7ab0cfe11ce..e4119a17a28 100644 --- a/pymatgen/io/xyz.py +++ b/pymatgen/io/xyz.py @@ -76,8 +76,8 @@ def _from_frame_string(contents) -> Molecule: def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(contents) -> XYZ: + @classmethod + def from_str(cls, contents) -> XYZ: """ Creates XYZ object from a string. @@ -99,10 +99,10 @@ def from_str(contents) -> XYZ: for xyz_match in pat.finditer(contents): xyz_text = xyz_match.group(0) mols.append(XYZ._from_frame_string(xyz_text)) - return XYZ(mols) + return cls(mols) - @staticmethod - def from_file(filename) -> XYZ: + @classmethod + def from_file(cls, filename) -> XYZ: """ Creates XYZ object from a file. @@ -113,7 +113,7 @@ def from_file(filename) -> XYZ: XYZ object """ with zopen(filename, "rt") as f: - return XYZ.from_str(f.read()) + return cls.from_str(f.read()) def as_dataframe(self): """ diff --git a/pymatgen/io/zeopp.py b/pymatgen/io/zeopp.py index be109ea0a48..48318ff08cd 100644 --- a/pymatgen/io/zeopp.py +++ b/pymatgen/io/zeopp.py @@ -96,8 +96,8 @@ def __str__(self): def from_string(cls, *args, **kwargs): return cls.from_str(*args, **kwargs) - @staticmethod - def from_str(string): + @classmethod + def from_str(cls, string): """ Reads a string representation to a ZeoCssr object. @@ -117,10 +117,10 @@ def from_str(string): lengths.insert(0, a) alpha = angles.pop(-1) angles.insert(0, alpha) - latt = Lattice.from_parameters(*lengths, *angles) + lattice = Lattice.from_parameters(*lengths, *angles) sp = [] coords = [] - chrg = [] + charge = [] for line in lines[4:]: m = re.match( r"\d+\s+(\w+)\s+([0-9\-\.]+)\s+([0-9\-\.]+)\s+([0-9\-\.]+)\s+(?:0\s+){8}([0-9\-\.]+)", @@ -131,11 +131,11 @@ def from_str(string): # coords.append([float(m.group(i)) for i in xrange(2, 5)]) # Zeo++ takes x-axis along a and pymatgen takes z-axis along c coords.append([float(m.group(i)) for i in [3, 4, 2]]) - chrg.append(m.group(5)) - return ZeoCssr(Structure(latt, sp, coords, site_properties={"charge": chrg})) + charge.append(m.group(5)) + return cls(Structure(lattice, sp, coords, site_properties={"charge": charge})) - @staticmethod - def from_file(filename): + @classmethod + def from_file(cls, filename): """ Reads a CSSR file to a ZeoCssr object. @@ -146,7 +146,7 @@ def from_file(filename): ZeoCssr object. """ with zopen(filename, "r") as f: - return ZeoCssr.from_str(f.read()) + return cls.from_str(f.read()) class ZeoVoronoiXYZ(XYZ): @@ -163,8 +163,8 @@ def __init__(self, mol): """ super().__init__(mol) - @staticmethod - def from_str(contents): + @classmethod + def from_str(cls, contents): """ Creates Zeo++ Voronoi XYZ object from a string. from_string method of XYZ class is being redefined. @@ -188,10 +188,10 @@ def from_str(contents): # coords.append(map(float, m.groups()[1:4])) # this is 0-indexed coords.append([float(j) for j in [m.group(i) for i in [3, 4, 2]]]) prop.append(float(m.group(5))) - return ZeoVoronoiXYZ(Molecule(sp, coords, site_properties={"voronoi_radius": prop})) + return cls(Molecule(sp, coords, site_properties={"voronoi_radius": prop})) - @staticmethod - def from_file(filename): + @classmethod + def from_file(cls, filename): """ Creates XYZ object from a file. @@ -202,7 +202,7 @@ def from_file(filename): XYZ object """ with zopen(filename) as f: - return ZeoVoronoiXYZ.from_str(f.read()) + return cls.from_str(f.read()) def __str__(self) -> str: output = [str(len(self._mols[0])), self._mols[0].composition.formula] diff --git a/pymatgen/phonon/thermal_displacements.py b/pymatgen/phonon/thermal_displacements.py index ff434ffbd81..d5f75506b81 100644 --- a/pymatgen/phonon/thermal_displacements.py +++ b/pymatgen/phonon/thermal_displacements.py @@ -489,9 +489,9 @@ def sort_order(site): return self.structure.copy(site_properties=site_properties) - @staticmethod + @classmethod def from_structure_with_site_properties_Ucif( - structure: Structure, temperature: float | None = None + cls, structure: Structure, temperature: float | None = None ) -> ThermalDisplacementMatrices: """Will create this object with the help of a structure with site properties. @@ -506,18 +506,9 @@ def from_structure_with_site_properties_Ucif( Ucif_matrix = [] # U11, U22, U33, U23, U13, U12 for site in structure: - Ucif_matrix.append( - [ - site.properties["U11_cif"], - site.properties["U22_cif"], - site.properties["U33_cif"], - site.properties["U23_cif"], - site.properties["U13_cif"], - site.properties["U12_cif"], - ] - ) + Ucif_matrix.append([site.properties[f"U{idx}_cif"] for idx in (11, 22, 33, 23, 13, 12)]) - return ThermalDisplacementMatrices.from_Ucif(Ucif_matrix, structure, temperature=temperature) + return cls.from_Ucif(Ucif_matrix, structure, temperature=temperature) @staticmethod def from_cif_P1(filename: str) -> list[ThermalDisplacementMatrices]: diff --git a/pymatgen/transformations/standard_transformations.py b/pymatgen/transformations/standard_transformations.py index 037fdf17700..2ca43ab1d0a 100644 --- a/pymatgen/transformations/standard_transformations.py +++ b/pymatgen/transformations/standard_transformations.py @@ -198,7 +198,7 @@ def is_one_to_many(self) -> bool: class SupercellTransformation(AbstractTransformation): - """The SupercellTransformation replicates an unitcell to a supercell.""" + """The SupercellTransformation replicates a unit cell to a supercell.""" def __init__(self, scaling_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1))): """ @@ -211,8 +211,8 @@ def __init__(self, scaling_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1))): """ self.scaling_matrix = scaling_matrix - @staticmethod - def from_scaling_factors(scale_a=1, scale_b=1, scale_c=1): + @classmethod + def from_scaling_factors(cls, scale_a=1, scale_b=1, scale_c=1): """Convenience method to get a SupercellTransformation from a simple series of three numbers for scaling each lattice vector. Equivalent to calling the normal with [[scale_a, 0, 0], [0, scale_b, 0], @@ -226,7 +226,7 @@ def from_scaling_factors(scale_a=1, scale_b=1, scale_c=1): Returns: SupercellTransformation. """ - return SupercellTransformation([[scale_a, 0, 0], [0, scale_b, 0], [0, 0, scale_c]]) + return cls([[scale_a, 0, 0], [0, scale_b, 0], [0, 0, scale_c]]) @staticmethod def from_boundary_distance( diff --git a/tests/io/test_adf.py b/tests/io/test_adf.py index c15dfce690a..878e9b0ddfe 100644 --- a/tests/io/test_adf.py +++ b/tests/io/test_adf.py @@ -38,14 +38,14 @@ END """ -h2oxyz = """3 +h2o_xyz = """3 0.0 O -0.90293455 0.66591421 0.0 H 0.05706545 0.66591421 0.0 H -1.22338913 1.57085004 0.0 """ -rhb18xyz = """19 +rhb18_xyz = """19 0.0 Rh -0.453396 -0.375115 0.000000 B 0.168139 3.232791 0.000000 @@ -70,7 +70,7 @@ def readfile(file_object): - """ + """` Return the content of the file as a string. Parameters @@ -170,7 +170,7 @@ def test_option_operations(self): def test_atom_block_key(self): block = AdfKey("atoms") - o = Molecule.from_str(h2oxyz, "xyz") + o = Molecule.from_str(h2o_xyz, "xyz") for site in o: block.add_subkey(AdfKey(str(site.specie), list(site.coords))) assert str(block) == atoms_string @@ -252,7 +252,7 @@ def test_serialization(self): class TestAdfInput(PymatgenTest): def test_main(self): tmp_file = f"{self.tmp_path}/adf.temp" - mol = Molecule.from_str(rhb18xyz, "xyz") + mol = Molecule.from_str(rhb18_xyz, "xyz") mol.set_charge_and_spin(-1, 3) task = AdfTask("optimize", **rhb18) inp = AdfInput(task)