From cc63b818beccf0090e16f603a9a2b04929092e39 Mon Sep 17 00:00:00 2001 From: "Haoyu (Daniel) YANG" Date: Thu, 14 Nov 2024 01:45:49 +0800 Subject: [PATCH] Replace hard-coded `np.allclose/isclose` and `math.isclose` (for complex expression) (#4164) * replace hard-coded math.isclose * add todo tag * replace more isclose in code * remove TODO tag * replace more in code * fix seemingly wrong quote position * avoid import when it's used only once or very few * replace last batch * revert change to isotropic check * replace some numpy isclose allclose * remove debug tag * remove some hard coded np allclose * revert some change on very simple evals * avoid unnecessary compare with zero * revert simple comparison * avoid minus zero * revert some simple expressions * simplify sci notation * revert simple comparisons * avoid 1.0e-x as it's already float * revert simple compare * use sci not * revert simple * use abs as we don't need always float * fix round usage * all close * revert as i'm not sure about the shape broadcasting * avoid import from numpy * clean up math import, reduce namespace cluster * simplify all close * sci notation * simplify import of math * simplify assert all close * simplify int(len(a) / b) to len(a) // b --- .../explicit_permutations_plane_algorithm.py | 2 +- src/pymatgen/analysis/bond_valence.py | 16 ++++---- .../coordination_environments/voronoi.py | 2 +- .../analysis/chemenv/utils/math_utils.py | 4 +- src/pymatgen/analysis/diffraction/neutron.py | 14 +++---- src/pymatgen/analysis/diffraction/xrd.py | 14 +++---- src/pymatgen/analysis/elasticity/elastic.py | 4 +- src/pymatgen/analysis/interface_reactions.py | 1 + src/pymatgen/analysis/local_env.py | 20 +++++----- src/pymatgen/analysis/magnetism/jahnteller.py | 5 ++- src/pymatgen/analysis/phase_diagram.py | 10 ++++- src/pymatgen/analysis/piezo.py | 2 +- src/pymatgen/analysis/piezo_sensitivity.py | 2 +- src/pymatgen/analysis/quasirrho.py | 4 +- src/pymatgen/analysis/reaction_calculator.py | 3 +- src/pymatgen/analysis/solar/slme.py | 3 +- src/pymatgen/analysis/structure_analyzer.py | 10 ++--- src/pymatgen/analysis/structure_matcher.py | 9 +++-- src/pymatgen/analysis/topological/spillage.py | 8 ++-- src/pymatgen/analysis/xas/spectrum.py | 1 + src/pymatgen/command_line/enumlib_caller.py | 4 +- src/pymatgen/core/composition.py | 6 +-- src/pymatgen/core/interface.py | 40 ++++++++++--------- src/pymatgen/core/ion.py | 2 +- src/pymatgen/core/lattice.py | 15 ++++--- src/pymatgen/core/operations.py | 16 ++++---- src/pymatgen/core/structure.py | 5 +-- src/pymatgen/core/surface.py | 29 +++++++------- src/pymatgen/core/tensors.py | 6 +-- .../electronic_structure/bandstructure.py | 4 +- .../electronic_structure/boltztrap.py | 2 +- src/pymatgen/io/abinit/abiobjects.py | 2 +- src/pymatgen/io/abinit/inputs.py | 6 +-- src/pymatgen/io/cif.py | 2 +- src/pymatgen/io/feff/sets.py | 15 +++---- src/pymatgen/io/icet.py | 2 +- src/pymatgen/io/lobster/lobsterenv.py | 4 +- src/pymatgen/io/openff.py | 2 +- src/pymatgen/io/qchem/outputs.py | 4 +- src/pymatgen/io/vasp/inputs.py | 2 +- src/pymatgen/io/vasp/sets.py | 8 ++-- src/pymatgen/io/xr.py | 17 ++++---- src/pymatgen/symmetry/analyzer.py | 2 +- src/pymatgen/symmetry/bandstructure.py | 2 +- src/pymatgen/symmetry/kpath.py | 19 +++++---- .../advanced_transformations.py | 11 +++-- .../transformations/site_transformations.py | 4 +- .../standard_transformations.py | 5 +-- src/pymatgen/util/coord.py | 2 +- .../coordination_environments/test_weights.py | 34 ++++++++-------- tests/analysis/test_local_env.py | 3 +- tests/analysis/test_reaction_calculator.py | 4 +- tests/core/test_bonds.py | 4 +- tests/electronic_structure/test_dos.py | 8 ++-- tests/entries/test_compatibility.py | 8 ++-- tests/io/qchem/test_utils.py | 2 +- tests/io/vasp/test_outputs.py | 2 +- .../test_standard_transformations.py | 3 +- 58 files changed, 226 insertions(+), 214 deletions(-) diff --git a/dev_scripts/chemenv/explicit_permutations_plane_algorithm.py b/dev_scripts/chemenv/explicit_permutations_plane_algorithm.py index eaef36e431f..87f7ec9088d 100644 --- a/dev_scripts/chemenv/explicit_permutations_plane_algorithm.py +++ b/dev_scripts/chemenv/explicit_permutations_plane_algorithm.py @@ -140,7 +140,7 @@ for icsm, csm in enumerate(csms): found = False for csm2 in csms_with_recorded_permutation: - if np.isclose(csm, csm2, rtol=0.0, atol=1.0e-6): + if np.isclose(csm, csm2, rtol=0.0, atol=1e-6): found = True break if not found: diff --git a/src/pymatgen/analysis/bond_valence.py b/src/pymatgen/analysis/bond_valence.py index eb2762bad36..8232d29b396 100644 --- a/src/pymatgen/analysis/bond_valence.py +++ b/src/pymatgen/analysis/bond_valence.py @@ -3,10 +3,10 @@ from __future__ import annotations import functools +import math import operator import os from collections import defaultdict -from math import exp, sqrt from typing import TYPE_CHECKING import numpy as np @@ -55,8 +55,8 @@ def calculate_bv_sum(site, nn_list, scale_factor=1.0): r2 = BV_PARAMS[el2]["r"] c1 = BV_PARAMS[el1]["c"] c2 = BV_PARAMS[el2]["c"] - R = r1 + r2 - r1 * r2 * (sqrt(c1) - sqrt(c2)) ** 2 / (c1 * r1 + c2 * r2) - vij = exp((R - nn.nn_distance * scale_factor) / 0.31) + R = r1 + r2 - r1 * r2 * (math.sqrt(c1) - math.sqrt(c2)) ** 2 / (c1 * r1 + c2 * r2) + vij = math.exp((R - nn.nn_distance * scale_factor) / 0.31) bv_sum += vij * (1 if el1.X < el2.X else -1) return bv_sum @@ -91,8 +91,8 @@ def calculate_bv_sum_unordered(site, nn_list, scale_factor=1): r2 = BV_PARAMS[el2]["r"] c1 = BV_PARAMS[el1]["c"] c2 = BV_PARAMS[el2]["c"] - R = r1 + r2 - r1 * r2 * (sqrt(c1) - sqrt(c2)) ** 2 / (c1 * r1 + c2 * r2) - vij = exp((R - nn.nn_distance * scale_factor) / 0.31) + R = r1 + r2 - r1 * r2 * (math.sqrt(c1) - math.sqrt(c2)) ** 2 / (c1 * r1 + c2 * r2) + vij = math.exp((R - nn.nn_distance * scale_factor) / 0.31) bv_sum += occu1 * occu2 * vij * (1 if el1.X < el2.X else -1) return bv_sum @@ -173,7 +173,7 @@ def _calc_site_probabilities(self, site, nn): sigma = data["std"] # Calculate posterior probability. Note that constant # factors are ignored. They have no effect on the results. - prob[sp.oxi_state] = exp(-((bv_sum - u) ** 2) / 2 / (sigma**2)) / sigma * PRIOR_PROB[sp] + prob[sp.oxi_state] = math.exp(-((bv_sum - u) ** 2) / 2 / (sigma**2)) / sigma * PRIOR_PROB[sp] # Normalize the probabilities try: prob = {k: v / sum(prob.values()) for k, v in prob.items()} @@ -194,7 +194,7 @@ def _calc_site_probabilities_unordered(self, site, nn): sigma = data["std"] # Calculate posterior probability. Note that constant # factors are ignored. They have no effect on the results. - prob[el][sp.oxi_state] = exp(-((bv_sum - u) ** 2) / 2 / (sigma**2)) / sigma * PRIOR_PROB[sp] + prob[el][sp.oxi_state] = math.exp(-((bv_sum - u) ** 2) / 2 / (sigma**2)) / sigma * PRIOR_PROB[sp] # Normalize the probabilities try: prob[el] = {k: v / sum(prob[el].values()) for k, v in prob[el].items()} @@ -263,7 +263,7 @@ def get_valences(self, structure: Structure): # Retain probabilities that are at least 1/100 of highest prob. filtered = list( filter( - lambda v: prob[elem.symbol][v] > 0.001 * prob[elem.symbol][val[0]], + lambda v: prob[elem.symbol][v] > 1e-3 * prob[elem.symbol][val[0]], val, ) ) diff --git a/src/pymatgen/analysis/chemenv/coordination_environments/voronoi.py b/src/pymatgen/analysis/chemenv/coordination_environments/voronoi.py index 4f030a52bea..d9100ec4e86 100644 --- a/src/pymatgen/analysis/chemenv/coordination_environments/voronoi.py +++ b/src/pymatgen/analysis/chemenv/coordination_environments/voronoi.py @@ -173,7 +173,7 @@ def setup_voronoi_list(self, indices, voronoi_cutoff): min_dist = min([min_dist, distances[ridge_point2]]) for iii, sss in enumerate(self.structure): - if neighbors[ridge_point2].is_periodic_image(sss, tolerance=1.0e-6): + if neighbors[ridge_point2].is_periodic_image(sss, tolerance=1e-6): idx = iii break results2.append( diff --git a/src/pymatgen/analysis/chemenv/utils/math_utils.py b/src/pymatgen/analysis/chemenv/utils/math_utils.py index 4422e98f5ba..bda7f548cbb 100644 --- a/src/pymatgen/analysis/chemenv/utils/math_utils.py +++ b/src/pymatgen/analysis/chemenv/utils/math_utils.py @@ -2,9 +2,9 @@ from __future__ import annotations +import math import operator from functools import reduce -from math import sqrt from typing import TYPE_CHECKING import numpy as np @@ -56,7 +56,7 @@ def prime_factors(n: int) -> list[int]: list of all prime factors of the given natural n. """ idx = 2 - while idx <= sqrt(n): + while idx <= math.sqrt(n): if n % idx == 0: lst = prime_factors(n // idx) lst.append(idx) diff --git a/src/pymatgen/analysis/diffraction/neutron.py b/src/pymatgen/analysis/diffraction/neutron.py index 64eaf5d8ea9..3cbdde411ac 100644 --- a/src/pymatgen/analysis/diffraction/neutron.py +++ b/src/pymatgen/analysis/diffraction/neutron.py @@ -3,8 +3,8 @@ from __future__ import annotations import json +import math import os -from math import asin, cos, degrees, pi, radians, sin from typing import TYPE_CHECKING import numpy as np @@ -96,7 +96,7 @@ def get_pattern(self, structure: Structure, scaled=True, two_theta_range=(0, 90) min_r, max_r = ( (0, 2 / wavelength) if two_theta_range is None - else [2 * sin(radians(t / 2)) / wavelength for t in two_theta_range] + else [2 * math.sin(math.radians(t / 2)) / wavelength for t in two_theta_range] ) # Obtain crystallographic reciprocal lattice points within range @@ -137,12 +137,12 @@ def get_pattern(self, structure: Structure, scaled=True, two_theta_range=(0, 90) for hkl, g_hkl, ind, _ in sorted(recip_pts, key=lambda i: (i[1], -i[0][0], -i[0][1], -i[0][2])): # Force miller indices to be integers - hkl = [int(round(i)) for i in hkl] + hkl = [round(i) for i in hkl] if g_hkl != 0: d_hkl = 1 / g_hkl # Bragg condition - theta = asin(wavelength * g_hkl / 2) + theta = math.asin(wavelength * g_hkl / 2) # s = sin(theta) / wavelength = 1 / 2d = |ghkl| / 2 (d = # 1/|ghkl|) @@ -158,15 +158,15 @@ def get_pattern(self, structure: Structure, scaled=True, two_theta_range=(0, 90) # Structure factor = sum of atomic scattering factors (with # position factor exp(2j * pi * g.r and occupancies). # Vectorized computation. - f_hkl = np.sum(coeffs * occus * np.exp(2j * pi * g_dot_r) * dw_correction) + f_hkl = np.sum(coeffs * occus * np.exp(2j * np.pi * g_dot_r) * dw_correction) # Lorentz polarization correction for hkl - lorentz_factor = 1 / (sin(theta) ** 2 * cos(theta)) + lorentz_factor = 1 / (math.sin(theta) ** 2 * math.cos(theta)) # Intensity for hkl is modulus square of structure factor i_hkl = (f_hkl * f_hkl.conjugate()).real - two_theta = degrees(2 * theta) + two_theta = math.degrees(2 * theta) if is_hex: # Use Miller-Bravais indices for hexagonal lattices diff --git a/src/pymatgen/analysis/diffraction/xrd.py b/src/pymatgen/analysis/diffraction/xrd.py index b0bf1984e26..1ce4efb9212 100644 --- a/src/pymatgen/analysis/diffraction/xrd.py +++ b/src/pymatgen/analysis/diffraction/xrd.py @@ -3,8 +3,8 @@ from __future__ import annotations import json +import math import os -from math import asin, cos, degrees, pi, radians, sin from typing import TYPE_CHECKING import numpy as np @@ -158,7 +158,7 @@ def get_pattern(self, structure: Structure, scaled=True, two_theta_range=(0, 90) min_r, max_r = ( (0, 2 / wavelength) if two_theta_range is None - else [2 * sin(radians(t / 2)) / wavelength for t in two_theta_range] + else [2 * math.sin(math.radians(t / 2)) / wavelength for t in two_theta_range] ) # Obtain crystallographic reciprocal lattice points within range @@ -201,10 +201,10 @@ def get_pattern(self, structure: Structure, scaled=True, two_theta_range=(0, 90) for hkl, g_hkl, ind, _ in sorted(recip_pts, key=lambda i: (i[1], -i[0][0], -i[0][1], -i[0][2])): # Force miller indices to be integers - hkl = [int(round(i)) for i in hkl] + hkl = [round(i) for i in hkl] if g_hkl != 0: # Bragg condition - theta = asin(wavelength * g_hkl / 2) + theta = math.asin(wavelength * g_hkl / 2) # s = sin(theta) / wavelength = 1 / 2d = |ghkl| / 2 (d = # 1/|ghkl|) @@ -235,15 +235,15 @@ def get_pattern(self, structure: Structure, scaled=True, two_theta_range=(0, 90) # Structure factor = sum of atomic scattering factors (with # position factor exp(2j * pi * g.r and occupancies). # Vectorized computation. - f_hkl = np.sum(fs * occus * np.exp(2j * pi * g_dot_r) * dw_correction) + f_hkl = np.sum(fs * occus * np.exp(2j * np.pi * g_dot_r) * dw_correction) # Lorentz polarization correction for hkl - lorentz_factor = (1 + cos(2 * theta) ** 2) / (sin(theta) ** 2 * cos(theta)) + lorentz_factor = (1 + math.cos(2 * theta) ** 2) / (math.sin(theta) ** 2 * math.cos(theta)) # Intensity for hkl is modulus square of structure factor i_hkl = (f_hkl * f_hkl.conjugate()).real - two_theta = degrees(2 * theta) + two_theta = math.degrees(2 * theta) if is_hex: # Use Miller-Bravais indices for hexagonal lattices diff --git a/src/pymatgen/analysis/elasticity/elastic.py b/src/pymatgen/analysis/elasticity/elastic.py index f28a320c622..4a236e24d3f 100644 --- a/src/pymatgen/analysis/elasticity/elastic.py +++ b/src/pymatgen/analysis/elasticity/elastic.py @@ -214,7 +214,7 @@ def directional_poisson_ratio(self, n: ArrayLike, m: ArrayLike, tol: float = 1e- tol (float): tolerance for testing of orthogonality """ n, m = get_uvec(n), get_uvec(m) - if not np.abs(np.dot(n, m)) < tol: + if np.abs(np.dot(n, m)) >= tol: raise ValueError("n and m must be orthogonal") v = self.compliance_tensor.einsum_sequence([n] * 2 + [m] * 2) v *= -1 / self.compliance_tensor.einsum_sequence([n] * 4) @@ -907,7 +907,7 @@ def find_eq_stress(strains, stresses, tol: float = 1e-10): eq_stress = stress_array[np.all(abs(strain_array) < tol, axis=(1, 2))] if eq_stress.size != 0: - all_same = (abs(eq_stress - eq_stress[0]) < 1e-8).all() + all_same = np.allclose(eq_stress, eq_stress[0], atol=1e-8, rtol=0) if len(eq_stress) > 1 and not all_same: raise ValueError( "Multiple stresses found for equilibrium strain" diff --git a/src/pymatgen/analysis/interface_reactions.py b/src/pymatgen/analysis/interface_reactions.py index 0fbde409a16..c9958be999a 100644 --- a/src/pymatgen/analysis/interface_reactions.py +++ b/src/pymatgen/analysis/interface_reactions.py @@ -150,6 +150,7 @@ def get_kinks(self) -> list[tuple[int, float, float, Reaction, float]]: critical_comp = self.pd.get_critical_compositions(self.comp1, self.comp2) x_kink, energy_kink, react_kink, energy_per_rxt_formula = [], [], [], [] + # TODO: perhaps a bad idea to use full equality to compare coords if (c1_coord == c2_coord).all(): x_kink = [0, 1] energy_kink = [self._get_energy(x) for x in x_kink] diff --git a/src/pymatgen/analysis/local_env.py b/src/pymatgen/analysis/local_env.py index b0d084186a6..12e89d3a608 100644 --- a/src/pymatgen/analysis/local_env.py +++ b/src/pymatgen/analysis/local_env.py @@ -127,8 +127,8 @@ def nearest_key(sorted_vals: list[int], skey: int) -> int: continue el = site.specie.symbol - oxi_state = int(round(site.specie.oxi_state)) - coord_no = int(round(vnn.get_cn(self._structure, idx))) + oxi_state = round(site.specie.oxi_state) + coord_no = round(vnn.get_cn(self._structure, idx)) try: tab_oxi_states = sorted(map(int, _ION_RADII[el])) oxi_state = nearest_key(tab_oxi_states, oxi_state) @@ -2888,9 +2888,9 @@ def get_order_parameters( if tol < 0.0: raise ValueError("Negative tolerance for weighted solid angle!") - left_of_unity = 1 - 1.0e-12 + left_of_unity = 1 - 1e-12 # The following threshold has to be adapted to non-Angstrom units. - very_small = 1.0e-12 + very_small = 1e-12 fac_bcc = 1 / math.exp(-0.5) # Find central site and its neighbors. @@ -3330,7 +3330,7 @@ def get_order_parameters( for j in range(n_neighbors): ops[idx] += sum(qsp_theta[idx][j]) tmp_norm += float(sum(norms[idx][j])) - ops[idx] = ops[idx] / tmp_norm if tmp_norm > 1.0e-12 else None # type: ignore[operator] + ops[idx] = ops[idx] / tmp_norm if tmp_norm > 1e-12 else None # type: ignore[operator] elif typ in { "T", @@ -3357,7 +3357,7 @@ def get_order_parameters( for j in range(n_neighbors): for k in range(len(qsp_theta[idx][j])): qsp_theta[idx][j][k] = ( - qsp_theta[idx][j][k] / norms[idx][j][k] if norms[idx][j][k] > 1.0e-12 else 0.0 + qsp_theta[idx][j][k] / norms[idx][j][k] if norms[idx][j][k] > 1e-12 else 0.0 ) ops[idx] = max(qsp_theta[idx][j]) if j == 0 else max(ops[idx], *qsp_theta[idx][j]) @@ -3436,7 +3436,7 @@ class BrunnerNNReciprocal(NearNeighbors): largest reciprocal gap in interatomic distances. """ - def __init__(self, tol: float = 1.0e-4, cutoff=8.0) -> None: + def __init__(self, tol: float = 1e-4, cutoff=8.0) -> None: """ Args: tol (float): tolerance parameter for bond determination @@ -3511,7 +3511,7 @@ class BrunnerNNRelative(NearNeighbors): of largest relative gap in interatomic distances. """ - def __init__(self, tol: float = 1.0e-4, cutoff=8.0) -> None: + def __init__(self, tol: float = 1e-4, cutoff=8.0) -> None: """ Args: tol (float): tolerance parameter for bond determination @@ -3587,7 +3587,7 @@ class BrunnerNNReal(NearNeighbors): largest gap in interatomic distances. """ - def __init__(self, tol: float = 1.0e-4, cutoff=8.0) -> None: + def __init__(self, tol: float = 1e-4, cutoff=8.0) -> None: """ Args: tol (float): tolerance parameter for bond determination @@ -3748,7 +3748,7 @@ def get_nn_info(self, structure: Structure, n: int): # calculate mean fictive ionic radius mefir = _get_mean_fictive_ionic_radius(firs) - # # iteratively solve MEFIR; follows equation 4 in Hoppe's EconN paper + # iteratively solve MEFIR; follows equation 4 in Hoppe's EconN paper prev_mefir = float("inf") while abs(prev_mefir - mefir) > 1e-4: # this is guaranteed to converge diff --git a/src/pymatgen/analysis/magnetism/jahnteller.py b/src/pymatgen/analysis/magnetism/jahnteller.py index 795d2a89e22..650ac15782b 100644 --- a/src/pymatgen/analysis/magnetism/jahnteller.py +++ b/src/pymatgen/analysis/magnetism/jahnteller.py @@ -2,6 +2,7 @@ from __future__ import annotations +import math import os import warnings from typing import TYPE_CHECKING, Literal, cast @@ -444,9 +445,9 @@ def _estimate_spin_state( # WARNING! this heuristic has not been robustly tested or benchmarked # using 'diff*0.25' as arbitrary measure, if known magmom is # too far away from expected value, we don't try to classify it - if known_magmom > mu_so_high or abs(mu_so_high - known_magmom) < diff * 0.25: + if known_magmom > mu_so_high or math.isclose(mu_so_high, known_magmom, abs_tol=diff * 0.25, rel_tol=0): return "high" - if known_magmom < mu_so_low or abs(mu_so_low - known_magmom) < diff * 0.25: + if known_magmom < mu_so_low or math.isclose(mu_so_low, known_magmom, abs_tol=diff * 0.25, rel_tol=0): return "low" return "unknown" diff --git a/src/pymatgen/analysis/phase_diagram.py b/src/pymatgen/analysis/phase_diagram.py index ae52c6aea54..3992aff63c6 100644 --- a/src/pymatgen/analysis/phase_diagram.py +++ b/src/pymatgen/analysis/phase_diagram.py @@ -1014,7 +1014,9 @@ def get_transition_chempots(self, element): clean_pots = [] for c in sorted(critical_chempots): - if len(clean_pots) == 0 or abs(c - clean_pots[-1]) > PhaseDiagram.numerical_tol: + if len(clean_pots) == 0 or not math.isclose( + c, clean_pots[-1], abs_tol=PhaseDiagram.numerical_tol, rel_tol=0 + ): clean_pots.append(c) clean_pots.reverse() return tuple(clean_pots) @@ -1996,7 +1998,11 @@ def fmt(fl): x = coeffs[-1] - if all(c >= -tol for c in coeffs) and (abs(sum(coeffs[:-1]) - 1) < tol) and (tol < x < 1 - tol): + if ( + all(c >= -tol for c in coeffs) + and (math.isclose(sum(coeffs[:-1]), 1, abs_tol=tol, rel_tol=0)) + and (tol < x < 1 - tol) + ): c1 = x / r1.num_atoms c2 = (1 - x) / r2.num_atoms factor = 1 / (c1 + c2) diff --git a/src/pymatgen/analysis/piezo.py b/src/pymatgen/analysis/piezo.py index 9d7c5793076..ef17f54de4e 100644 --- a/src/pymatgen/analysis/piezo.py +++ b/src/pymatgen/analysis/piezo.py @@ -38,7 +38,7 @@ def __new__(cls, input_array: ArrayLike, tol: float = 1e-3) -> Self: representing the piezo tensor """ obj = super().__new__(cls, input_array, check_rank=3) - if not (obj - np.transpose(obj, (0, 2, 1)) < tol).all(): + if not np.allclose(obj, np.transpose(obj, (0, 2, 1)), atol=tol, rtol=0): warnings.warn("Input piezo tensor does not satisfy standard symmetries") return obj.view(cls) diff --git a/src/pymatgen/analysis/piezo_sensitivity.py b/src/pymatgen/analysis/piezo_sensitivity.py index 254fc577c37..0733a818a28 100644 --- a/src/pymatgen/analysis/piezo_sensitivity.py +++ b/src/pymatgen/analysis/piezo_sensitivity.py @@ -181,7 +181,7 @@ def __init__(self, structure: Structure, ist, pointops, tol: float = 1e-3): self.IST_operations: list[list[list]] = [] obj = self.ist - if not (obj - np.transpose(obj, (0, 1, 3, 2)) < tol).all(): + if not np.allclose(obj, np.transpose(obj, (0, 1, 3, 2)), atol=tol, rtol=0): warnings.warn("Input internal strain tensor does not satisfy standard symmetries") def get_IST_operations(self, opstol=1e-3) -> list[list[list]]: diff --git a/src/pymatgen/analysis/quasirrho.py b/src/pymatgen/analysis/quasirrho.py index 3435ccfefd2..8a035be747e 100644 --- a/src/pymatgen/analysis/quasirrho.py +++ b/src/pymatgen/analysis/quasirrho.py @@ -10,7 +10,7 @@ from __future__ import annotations -from math import isclose +import math from typing import TYPE_CHECKING import numpy as np @@ -221,7 +221,7 @@ def _get_quasirrho_thermo( linear = True for coord in coords[1:]: theta = abs(np.dot(coord - coords[0], v0) / np.linalg.norm(coord - coords[0]) / np.linalg.norm(v0)) - if not isclose(theta, 1, abs_tol=1e-4): + if not math.isclose(theta, 1, abs_tol=1e-4): linear = False # Rotational component of Entropy and Energy diff --git a/src/pymatgen/analysis/reaction_calculator.py b/src/pymatgen/analysis/reaction_calculator.py index ca38a2e0cfd..7266301eb05 100644 --- a/src/pymatgen/analysis/reaction_calculator.py +++ b/src/pymatgen/analysis/reaction_calculator.py @@ -2,6 +2,7 @@ from __future__ import annotations +import math import re from itertools import chain, combinations from typing import TYPE_CHECKING, overload @@ -208,7 +209,7 @@ def _str_from_formulas(cls, coeffs, formulas) -> str: reactant_str = [] product_str = [] for amt, formula in zip(coeffs, formulas, strict=True): - if abs(amt + 1) < cls.TOLERANCE: + if math.isclose(amt, -1, abs_tol=cls.TOLERANCE, rel_tol=0): reactant_str.append(formula) elif abs(amt - 1) < cls.TOLERANCE: product_str.append(formula) diff --git a/src/pymatgen/analysis/solar/slme.py b/src/pymatgen/analysis/solar/slme.py index 3e5b9afd96b..6b1be149831 100644 --- a/src/pymatgen/analysis/solar/slme.py +++ b/src/pymatgen/analysis/solar/slme.py @@ -11,7 +11,6 @@ from __future__ import annotations import os -from math import pi import matplotlib.pyplot as plt import numpy as np @@ -110,7 +109,7 @@ def absorption_coefficient(dielectric): ( 2.0 * np.sqrt(2.0) - * pi + * np.pi * eV_to_recip_cm * energies_in_eV * np.sqrt(-epsilon_1 + np.sqrt(epsilon_1**2 + epsilon_2**2)) diff --git a/src/pymatgen/analysis/structure_analyzer.py b/src/pymatgen/analysis/structure_analyzer.py index b88e93a6146..14c67ebaae4 100644 --- a/src/pymatgen/analysis/structure_analyzer.py +++ b/src/pymatgen/analysis/structure_analyzer.py @@ -3,8 +3,8 @@ from __future__ import annotations import itertools +import math from collections import defaultdict -from math import acos, pi from typing import TYPE_CHECKING from warnings import warn @@ -249,7 +249,7 @@ def __init__(self, structure: Structure, cutoff=10): self.cutoff = cutoff self.structure = structure recip_vec = np.array(self.structure.lattice.reciprocal_lattice.abc) - cutoff_vec = np.ceil(cutoff * recip_vec / (2 * pi)) + cutoff_vec = np.ceil(cutoff * recip_vec / (2 * np.pi)) offsets = np.mgrid[ -cutoff_vec[0] : cutoff_vec[0] + 1, -cutoff_vec[1] : cutoff_vec[1] + 1, @@ -356,9 +356,9 @@ def solid_angle(center, coords): v = -np.dot(cross_products[i], cross_products[i + 1]) / ( np.linalg.norm(cross_products[i]) * np.linalg.norm(cross_products[i + 1]) ) - vals.append(acos(np.clip(v, -1, 1))) + vals.append(math.acos(np.clip(v, -1, 1))) phi = sum(vals) - return phi + (3 - len(radii)) * pi + return phi + (3 - len(radii)) * np.pi def get_max_bond_lengths(structure, el_radius_updates=None): @@ -456,7 +456,7 @@ def parse_oxide(self) -> tuple[str, int]: if h_sites_frac_coords: dist_matrix = lattice.get_all_distances(o_sites_frac_coords, h_sites_frac_coords) if np.any(dist_matrix < relative_cutoff * 0.93): - return "hydroxide", int(len(np.where(dist_matrix < relative_cutoff * 0.93)[0]) / 2) + return "hydroxide", len(np.where(dist_matrix < relative_cutoff * 0.93)[0]) // 2 dist_matrix = lattice.get_all_distances(o_sites_frac_coords, o_sites_frac_coords) np.fill_diagonal(dist_matrix, 1000) is_superoxide = is_peroxide = is_ozonide = False diff --git a/src/pymatgen/analysis/structure_matcher.py b/src/pymatgen/analysis/structure_matcher.py index 3d0885d254e..0eae9c5499a 100644 --- a/src/pymatgen/analysis/structure_matcher.py +++ b/src/pymatgen/analysis/structure_matcher.py @@ -4,6 +4,7 @@ import abc import itertools +import math from functools import lru_cache from typing import TYPE_CHECKING, cast @@ -441,9 +442,9 @@ def _get_supercell_size(self, s1, s2): raise ValueError("Invalid argument for supercell_size.") if fu < 2 / 3: - return int(round(1 / fu)), False + return round(1 / fu), False - return int(round(fu)), True + return round(fu), True def _get_lattices(self, target_lattice, s, supercell_size=1): """ @@ -463,7 +464,7 @@ def _get_lattices(self, target_lattice, s, supercell_size=1): skip_rotation_matrix=True, ) for latt, _, scale_m in lattices: - if abs(abs(np.linalg.det(scale_m)) - supercell_size) < 0.5: + if math.isclose(abs(np.linalg.det(scale_m)), supercell_size, abs_tol=0.5, rel_tol=0): yield latt, scale_m def _get_supercells(self, struct1, struct2, fu, s1_supercell): @@ -1203,7 +1204,7 @@ def get_s2_like_s1(self, struct1, struct2, include_ignored_species=True): sites = [temp.sites[i] for i in mapping if i is not None] if include_ignored_species: - start = int(round(len(temp) / len(struct2) * len(s2))) + start = round(len(temp) / len(struct2) * len(s2)) sites.extend(temp.sites[start:]) return Structure.from_sites(sites) diff --git a/src/pymatgen/analysis/topological/spillage.py b/src/pymatgen/analysis/topological/spillage.py index a29b5446c9c..c88a317f9e4 100644 --- a/src/pymatgen/analysis/topological/spillage.py +++ b/src/pymatgen/analysis/topological/spillage.py @@ -90,11 +90,11 @@ def overlap_so_spinpol(self): n_elec_list.append([cup, cdn, cup + cdn]) n_arr = np.array(n_elec_list) - n_up = int(round(np.mean(n_arr[:, 0]))) - n_dn = int(round(np.mean(n_arr[:, 1]))) - n_tot = int(round(np.mean(n_arr[:, 2]))) + n_up = round(np.mean(n_arr[:, 0])) + n_dn = round(np.mean(n_arr[:, 1])) + n_tot = round(np.mean(n_arr[:, 2])) - n_elec = int(n_tot) + n_elec = n_tot # noso_homo_up = np.max(noso_bands[0, :, n_up - 1]) # noso_lumo_up = np.min(noso_bands[0, :, n_up]) diff --git a/src/pymatgen/analysis/xas/spectrum.py b/src/pymatgen/analysis/xas/spectrum.py index daba9b8d2c9..3fd22671448 100644 --- a/src/pymatgen/analysis/xas/spectrum.py +++ b/src/pymatgen/analysis/xas/spectrum.py @@ -203,6 +203,7 @@ def stitch(self, other: XAS, num_samples: int = 500, mode: Literal["XAFS", "L23" warnings.warn( "There might exist a jump at the L2 and L3-edge junction.", UserWarning, + stacklevel=2, ) return XAS(energy, mu, self.structure, self.absorbing_element, "L23", "XANES") diff --git a/src/pymatgen/command_line/enumlib_caller.py b/src/pymatgen/command_line/enumlib_caller.py index f892a81e755..a92ebed3041 100644 --- a/src/pymatgen/command_line/enumlib_caller.py +++ b/src/pymatgen/command_line/enumlib_caller.py @@ -269,7 +269,7 @@ def get_sg_info(ss): conc = amt / total_amounts if abs(conc * base - round(conc * base)) < 1e-5: - output.append(f"{int(round(conc * base))} {int(round(conc * base))} {base}") + output.append(f"{round(conc * base)} {round(conc * base)} {base}") else: min_conc = math.floor(conc * base) output.append(f"{min_conc - 1} {min_conc + 1} {base}") @@ -372,7 +372,7 @@ def _get_structures(self, num_structs): if len(self.ordered_sites) > 0: transformation = np.dot(new_latt.matrix, inv_org_latt) - transformation = [[int(round(cell)) for cell in row] for row in transformation] + transformation = [[round(cell) for cell in row] for row in transformation] logger.debug(f"Supercell matrix: {transformation}") struct = ordered_structure * transformation sites.extend([site.to_unit_cell() for site in struct]) diff --git a/src/pymatgen/core/composition.py b/src/pymatgen/core/composition.py index 47984444451..319073c0640 100644 --- a/src/pymatgen/core/composition.py +++ b/src/pymatgen/core/composition.py @@ -5,6 +5,7 @@ from __future__ import annotations import collections +import math import os import re import string @@ -12,7 +13,6 @@ from collections import defaultdict from functools import total_ordering from itertools import combinations_with_replacement, product -from math import isnan from typing import TYPE_CHECKING, cast from monty.fractions import gcd, gcd_float @@ -129,7 +129,7 @@ def __init__(self, *args, strict: bool = False, **kwargs) -> None: elem_map = args[0] elif len(args) == 1 and isinstance(args[0], str): elem_map = self._parse_formula(args[0]) # type: ignore[assignment] - elif len(args) == 1 and isinstance(args[0], float) and isnan(args[0]): + elif len(args) == 1 and isinstance(args[0], float) and math.isnan(args[0]): raise ValueError("float('NaN') is not a valid Composition, did you mean 'NaN'?") else: elem_map = dict(*args, **kwargs) # type: ignore[assignment] @@ -400,7 +400,7 @@ def get_reduced_formula_and_factor(self, iupac_ordering: bool = False) -> tuple[ all_int = all(abs(val - round(val)) < type(self).amount_tolerance for val in self.values()) if not all_int: return self.formula.replace(" ", ""), 1 - el_amt_dict = {key: int(round(val)) for key, val in self.get_el_amt_dict().items()} + el_amt_dict = {key: round(val) for key, val in self.get_el_amt_dict().items()} formula, factor = reduce_formula(el_amt_dict, iupac_ordering=iupac_ordering) if formula in type(self).special_formulas: diff --git a/src/pymatgen/core/interface.py b/src/pymatgen/core/interface.py index 823d009849f..45c34ff3e0e 100644 --- a/src/pymatgen/core/interface.py +++ b/src/pymatgen/core/interface.py @@ -220,7 +220,7 @@ def get_sorted_structure( @property def sigma(self) -> int: """The sigma value of the GB. If using 'quick_gen' to generate GB, this value is not valid.""" - return int(round(self.oriented_unit_cell.volume / self.init_cell.volume)) + return round(self.oriented_unit_cell.volume / self.init_cell.volume) @property def sigma_from_site_prop(self) -> int: @@ -400,7 +400,7 @@ def gb_from_parameters( ratio: list[int] | None = None, plane: Tuple3Ints | None = None, max_search: int = 20, - tol_coi: float = 1.0e-8, + tol_coi: float = 1e-8, rm_ratio: float = 0.7, quick_gen: bool = False, ) -> GrainBoundary: @@ -470,11 +470,11 @@ def gb_from_parameters( convention_cell = analyzer.get_conventional_standard_structure() vol_ratio = self.initial_structure.volume / convention_cell.volume # BCC primitive cell, belong to cubic system - if abs(vol_ratio - 0.5) < 1.0e-3: + if abs(vol_ratio - 0.5) < 1e-3: trans_cry = np.array([[0.5, 0.5, -0.5], [-0.5, 0.5, 0.5], [0.5, -0.5, 0.5]]) logger.info("Make sure this is for cubic with bcc primitive cell") # FCC primitive cell, belong to cubic system - elif abs(vol_ratio - 0.25) < 1.0e-3: + elif abs(vol_ratio - 0.25) < 1e-3: trans_cry = np.array([[0.5, 0.5, 0], [0, 0.5, 0.5], [0.5, 0, 0.5]]) logger.info("Make sure this is for cubic with fcc primitive cell") else: @@ -1047,7 +1047,7 @@ def get_trans_mat( mu = round(mu / temp) mv = round(mv / temp) d = (u**2 + v**2 - u * v) * mv + w**2 * mu - if abs(angle - 180.0) < 1.0e0: + if abs(angle - 180.0) < 1: m = 0 n = 1 else: @@ -1106,7 +1106,7 @@ def get_trans_mat( mu = round(mu / temp) mv = round(mv / temp) d = (u**2 + v**2 + w**2) * (mu - 2 * mv) + 2 * mv * (v * w + w * u + u * v) - if abs(angle - 180.0) < 1.0e0: + if abs(angle - 180.0) < 1: m = 0 n = 1 else: @@ -1224,7 +1224,7 @@ def get_trans_mat( mv = round(mv / temp) lam = round(lam / temp) d = (mv * u**2 + lam * v**2) * mv + w**2 * mu * mv - if abs(angle - 180.0) < 1.0e0: + if abs(angle - 180.0) < 1: m = 0 n = 1 else: @@ -1948,9 +1948,9 @@ def enum_possible_plane_cubic( miller2 = GrainBoundaryGenerator.vec_to_surface(vec) if np.all(np.abs(np.array(miller2)) <= plane_cutoff): cos_1 = abs(np.dot(val, r_axis) / np.linalg.norm(val) / np.linalg.norm(r_axis)) - if 1 - cos_1 < 1.0e-5: + if 1 - cos_1 < 1e-5: all_combinations["Twist"].append([list(val), miller2]) - elif cos_1 < 1.0e-8: + elif cos_1 < 1e-8: sym_tilt = False if np.sum(np.abs(val)) == np.sum(np.abs(miller2)): ave = (np.array(val) + np.array(miller2)) / 2 @@ -1958,7 +1958,7 @@ def enum_possible_plane_cubic( for plane in sym_plane: cos_2 = abs(np.dot(ave, plane) / np.linalg.norm(ave) / np.linalg.norm(plane)) cos_3 = abs(np.dot(ave1, plane) / np.linalg.norm(ave1) / np.linalg.norm(plane)) - if 1 - cos_2 < 1.0e-5 or 1 - cos_3 < 1.0e-5: + if 1 - cos_2 < 1e-5 or 1 - cos_3 < 1e-5: all_combinations["Symmetric tilt"].append([list(val), miller2]) sym_tilt = True break @@ -2207,7 +2207,7 @@ def slab_from_csl( for ii in combination: # type: ignore[assignment] if reduce(math.gcd, ii) == 1: temp = np.dot(np.array(ii), csl) - if abs(np.dot(temp, surface) - 0) < 1.0e-8: + if abs(np.dot(temp, surface)) < 1e-8: ab_vector.append(temp) else: # c vector length along the direction perpendicular to surface @@ -2216,7 +2216,7 @@ def slab_from_csl( c_norm_temp = np.linalg.norm(np.matmul(temp, trans)) if normal: c_cross = np.cross(np.matmul(temp, trans), np.matmul(surface, ctrans)) - if np.linalg.norm(c_cross) < 1.0e-8: + if np.linalg.norm(c_cross) < 1e-8: if normal_init: if c_norm_temp < c_norm: t_matrix[2] = temp @@ -2225,7 +2225,9 @@ def slab_from_csl( c_norm = c_norm_temp normal_init = True t_matrix[2] = temp - elif c_len_temp < c_length or (abs(c_len_temp - c_length) < 1.0e-8 and c_norm_temp < c_norm): + elif c_len_temp < c_length or ( + math.isclose(c_len_temp, c_length, abs_tol=1e-8, rel_tol=0) and c_norm_temp < c_norm + ): t_matrix[2] = temp c_norm = c_norm_temp c_length = c_len_temp @@ -2256,9 +2258,9 @@ def slab_from_csl( for ii in combination: # type: ignore[assignment] if reduce(math.gcd, ii) == 1: temp = np.dot(np.array(ii), csl) - if abs(np.dot(temp, surface) - 0) > 1.0e-8: + if abs(np.dot(temp, surface)) > 1e-8: c_cross = np.cross(np.matmul(temp, trans), np.matmul(surface, ctrans)) - if np.linalg.norm(c_cross) < 1.0e-8: + if np.linalg.norm(c_cross) < 1e-8: # c vector length itself c_norm_temp = np.linalg.norm(np.matmul(temp, trans)) if normal_init: @@ -2276,7 +2278,7 @@ def slab_from_csl( ab_norm = None for ii in combinations(ab_vector, 2): area_temp = np.linalg.norm(np.cross(np.matmul(ii[0], trans), np.matmul(ii[1], trans))) - if abs(area_temp - 0) > 1.0e-8: + if abs(area_temp) > 1e-8: ab_norm_temp = np.linalg.norm(np.matmul(ii[0], trans)) + np.linalg.norm(np.matmul(ii[1], trans)) if area is None: area = area_temp @@ -2284,7 +2286,9 @@ def slab_from_csl( t_matrix[0] = ii[0] t_matrix[1] = ii[1] - elif area_temp < area or (abs(area - area_temp) < 1.0e-8 and ab_norm_temp < ab_norm): + elif area_temp < area or ( + math.isclose(area, area_temp, abs_tol=1e-8, rel_tol=0) and ab_norm_temp < ab_norm + ): t_matrix[0] = ii[0] t_matrix[1] = ii[1] area = area_temp @@ -2348,7 +2352,7 @@ def vec_to_surface(vec: Vector3D) -> MillerIndex: miller: list[None | int] = [None] * 3 index = [] for idx, value in enumerate(vec): - if abs(value) < 1.0e-8: + if abs(value) < 1e-8: miller[idx] = 0 else: index.append(idx) diff --git a/src/pymatgen/core/ion.py b/src/pymatgen/core/ion.py index dd8eb4ebfa5..6286cbf1217 100644 --- a/src/pymatgen/core/ion.py +++ b/src/pymatgen/core/ion.py @@ -171,7 +171,7 @@ def get_reduced_formula_and_factor( nH2O = int(nO) if nH >= 2 * nO else int(nH) // 2 comp = self.composition - nH2O * Composition("H2O") - el_amt_dict = {k: int(round(v)) for k, v in comp.get_el_amt_dict().items()} + el_amt_dict = {k: round(v) for k, v in comp.get_el_amt_dict().items()} formula, factor = reduce_formula(el_amt_dict, iupac_ordering=iupac_ordering) # This line checks specifically that the contains an equal amount of O and H. When that is the case, diff --git a/src/pymatgen/core/lattice.py b/src/pymatgen/core/lattice.py index 056f3077533..a49399b09c4 100644 --- a/src/pymatgen/core/lattice.py +++ b/src/pymatgen/core/lattice.py @@ -971,9 +971,9 @@ def get_angles(v1, v2, l1, l2): f_a, f_b, f_c = (frac[i] for i in inds) l_a, l_b, l_c = (np.sum(c**2, axis=-1) ** 0.5 for c in (c_a, c_b, c_c)) - alpha_b = np.abs(get_angles(c_b, c_c, l_b, l_c) - alpha) < atol - beta_b = np.abs(get_angles(c_a, c_c, l_a, l_c) - beta) < atol - gamma_b = np.abs(get_angles(c_a, c_b, l_a, l_b) - gamma) < atol + alpha_b = np.isclose(get_angles(c_b, c_c, l_b, l_c), alpha, atol=atol, rtol=0) + beta_b = np.isclose(get_angles(c_a, c_c, l_a, l_c), beta, atol=atol, rtol=0) + gamma_b = np.isclose(get_angles(c_a, c_b, l_a, l_b), gamma, atol=atol, rtol=0) for idx, all_j in enumerate(gamma_b): inds = np.logical_and(all_j[:, None], np.logical_and(alpha_b, beta_b[idx][None, :])) @@ -1640,15 +1640,18 @@ def is_hexagonal( """ lengths = self.lengths angles = self.angles - right_angles = [i for i in range(3) if abs(angles[i] - 90) < hex_angle_tol] + right_angles = [i for i in range(3) if math.isclose(angles[i], 90, abs_tol=hex_angle_tol, rel_tol=0)] hex_angles = [ - idx for idx in range(3) if abs(angles[idx] - 60) < hex_angle_tol or abs(angles[idx] - 120) < hex_angle_tol + idx + for idx in range(3) + if math.isclose(angles[idx], 60, abs_tol=hex_angle_tol, rel_tol=0) + or math.isclose(angles[idx], 120, abs_tol=hex_angle_tol, rel_tol=0) ] return ( len(right_angles) == 2 and len(hex_angles) == 1 - and abs(lengths[right_angles[0]] - lengths[right_angles[1]]) < hex_length_tol + and math.isclose(lengths[right_angles[0]], lengths[right_angles[1]], abs_tol=hex_length_tol, rel_tol=0) ) def get_distance_and_image( diff --git a/src/pymatgen/core/operations.py b/src/pymatgen/core/operations.py index d395c2c3d3b..4da97bd5b6a 100644 --- a/src/pymatgen/core/operations.py +++ b/src/pymatgen/core/operations.py @@ -3,10 +3,10 @@ from __future__ import annotations import copy +import math import re import string import warnings -from math import cos, pi, sin, sqrt from typing import TYPE_CHECKING, Literal, cast import numpy as np @@ -280,9 +280,9 @@ def from_axis_angle_and_translation( vec = np.asarray(translation_vec) - ang = angle if angle_in_radians else angle * pi / 180 - cos_a = cos(ang) - sin_a = sin(ang) + ang = angle if angle_in_radians else angle * np.pi / 180 + cos_a = math.cos(ang) + sin_a = math.sin(ang) unit_vec = axis / np.linalg.norm(axis) rot_mat = np.zeros((3, 3)) rot_mat[0, 0] = cos_a + unit_vec[0] ** 2 * (1 - cos_a) @@ -318,15 +318,15 @@ def from_origin_axis_angle( Returns: SymmOp. """ - theta = angle if angle_in_radians else angle * pi / 180 + theta = angle if angle_in_radians else angle * np.pi / 180 a, b, c = origin ax_u, ax_v, ax_w = axis # Set some intermediate values. u2, v2, w2 = ax_u * ax_u, ax_v * ax_v, ax_w * ax_w - cos_t = cos(theta) - sin_t = sin(theta) + cos_t = math.cos(theta) + sin_t = math.sin(theta) l2 = u2 + v2 + w2 - lsqrt = sqrt(l2) + lsqrt = math.sqrt(l2) # Build the matrix entries element by element. m11 = (u2 + (v2 + w2) * cos_t) / l2 diff --git a/src/pymatgen/core/structure.py b/src/pymatgen/core/structure.py index 98a3f0fa8b7..8e46793c837 100644 --- a/src/pymatgen/core/structure.py +++ b/src/pymatgen/core/structure.py @@ -28,7 +28,6 @@ from monty.dev import deprecated from monty.io import zopen from monty.json import MSONable -from numpy import cross, eye from numpy.linalg import norm from ruamel.yaml import YAML from scipy.cluster.hierarchy import fcluster, linkage @@ -4614,7 +4613,7 @@ def rotate_sites( theta %= 2 * np.pi - rm = expm(cross(eye(3), axis / norm(axis)) * theta) + rm = expm(np.cross(np.eye(3), axis / norm(axis)) * theta) for idx in indices: site = self[idx] coords = ((np.dot(rm, np.array(site.coords - anchor).T)).T + anchor).ravel() @@ -5214,7 +5213,7 @@ def rotate_sites( theta %= 2 * np.pi - rm = expm(cross(eye(3), axis / norm(axis)) * theta) + rm = expm(np.cross(np.eye(3), axis / norm(axis)) * theta) for idx in indices: site = self[idx] diff --git a/src/pymatgen/core/surface.py b/src/pymatgen/core/surface.py index ed7a7d4d6d8..f7f692b6a74 100644 --- a/src/pymatgen/core/surface.py +++ b/src/pymatgen/core/surface.py @@ -22,7 +22,6 @@ import os import warnings from functools import reduce -from math import gcd, isclose from typing import TYPE_CHECKING, cast import numpy as np @@ -425,7 +424,7 @@ def get_symmetric_site( for op in ops: slab = self.copy() site_other = op.operate(point) - if isclose(site_other[2], point[2], abs_tol=1e-6): + if math.isclose(site_other[2], point[2], abs_tol=1e-6): continue # Add dummy sites to check if the overall structure is symmetric @@ -507,8 +506,8 @@ def get_equi_index(site: PeriodicSite) -> int: # Determine what fraction the slab is of the total cell size in the # c direction. Round to nearest rational number. - n_layers_total = int(round(self.lattice.c / self.oriented_unit_cell.lattice.c)) - n_layers_slab = int(round((sorted_csites[-1].c - sorted_csites[0].c) * n_layers_total)) + n_layers_total = round(self.lattice.c / self.oriented_unit_cell.lattice.c) + n_layers_slab = round((sorted_csites[-1].c - sorted_csites[0].c) * n_layers_total) slab_ratio = n_layers_slab / n_layers_total spg_analyzer = SpacegroupAnalyzer(self) @@ -521,7 +520,7 @@ def get_equi_index(site: PeriodicSite) -> int: to_move = [] fixed = [] for site in sites: - if abs(site.c - surface_site.c) < tol and ( + if math.isclose(site.c, surface_site.c, abs_tol=tol) and ( (not same_species_only) or site.species == surface_site.species ): to_move.append(site) @@ -543,7 +542,7 @@ def get_equi_index(site: PeriodicSite) -> int: continue combinations = [] for g in grouped: - combinations.append(list(itertools.combinations(g, int(len(g) / 2)))) + combinations.append(list(itertools.combinations(g, len(g) // 2))) for selection in itertools.product(*combinations): species = [site.species for site in fixed] @@ -948,7 +947,7 @@ def __init__( def reduce_vector(vector: MillerIndex) -> MillerIndex: """Helper function to reduce vectors.""" - divisor = abs(reduce(gcd, vector)) # type: ignore[arg-type] + divisor = abs(reduce(math.gcd, vector)) # type: ignore[arg-type] return cast(Tuple3Ints, tuple(int(idx / divisor) for idx in vector)) def add_site_types() -> None: @@ -1003,8 +1002,8 @@ def calculate_scaling_factor() -> np.ndarray: lcm_miller = lcm(*(miller_index[i] for i, _d in non_orth_ind)) for (ii, _di), (jj, _dj) in itertools.combinations(non_orth_ind, 2): scale_factor = [0, 0, 0] - scale_factor[ii] = -int(round(lcm_miller / miller_index[ii])) - scale_factor[jj] = int(round(lcm_miller / miller_index[jj])) + scale_factor[ii] = -round(lcm_miller / miller_index[ii]) + scale_factor[jj] = round(lcm_miller / miller_index[jj]) slab_scale_factor.append(scale_factor) if len(slab_scale_factor) == 2: break @@ -1025,7 +1024,7 @@ def calculate_scaling_factor() -> np.ndarray: cosine = abs(np.dot(vec, normal) / osdm) candidates.append((uvw, cosine, osdm)) # Stop searching if cosine equals 1 or -1 - if isclose(abs(cosine), 1, abs_tol=1e-8): + if math.isclose(abs(cosine), 1, abs_tol=1e-8): break # We want the indices with the maximum absolute cosine, # but smallest possible length. @@ -1323,7 +1322,7 @@ def get_z_ranges( z_ranges.extend([(0, z_range[1]), (z_range[0] + 1, 1)]) # Neglect overlapping positions - elif not isclose(z_range[0], z_range[1], abs_tol=ztol): + elif not math.isclose(z_range[0], z_range[1], abs_tol=ztol): z_ranges.append(z_range) return z_ranges @@ -1707,7 +1706,7 @@ def get_d(slab: Slab) -> float: distance = None for site, next_site in itertools.pairwise(sorted_sites): - if not isclose(site.frac_coords[2], next_site.frac_coords[2], abs_tol=1e-6): + if not math.isclose(site.frac_coords[2], next_site.frac_coords[2], abs_tol=1e-6): distance = next_site.frac_coords[2] - site.frac_coords[2] break @@ -2070,7 +2069,7 @@ def get_symmetrically_distinct_miller_indices( unique_millers_conv: list = [] for idx, miller in enumerate(miller_list): - denom = abs(reduce(gcd, miller)) # type: ignore[arg-type] + denom = abs(reduce(math.gcd, miller)) # type: ignore[arg-type] miller = cast(Tuple3Ints, tuple(int(idx / denom) for idx in miller)) if not _is_in_miller_family(miller, unique_millers, symm_ops): if spg_analyzer.get_crystal_system() == "trigonal": @@ -2078,7 +2077,7 @@ def get_symmetrically_distinct_miller_indices( # the primitive symmetry operations and their # corresponding hkls in the conventional setting unique_millers.append(miller) - denom = abs(reduce(gcd, conv_hkl_list[idx])) # type: ignore[arg-type] + denom = abs(reduce(math.gcd, conv_hkl_list[idx])) # type: ignore[arg-type] cmiller = tuple(int(idx / denom) for idx in conv_hkl_list[idx]) unique_millers_conv.append(cmiller) else: @@ -2129,7 +2128,7 @@ def math_lcm(a: int, b: int) -> int: # Perform the transformation transf_hkl = np.dot(reduced_transf, miller_index) - divisor = abs(reduce(gcd, transf_hkl)) # type: ignore[arg-type] + divisor = abs(reduce(math.gcd, transf_hkl)) # type: ignore[arg-type] transf_hkl = np.array([idx // divisor for idx in transf_hkl]) # Get positive Miller index diff --git a/src/pymatgen/core/tensors.py b/src/pymatgen/core/tensors.py index 7ddfb3d35a9..f2c72cc9a37 100644 --- a/src/pymatgen/core/tensors.py +++ b/src/pymatgen/core/tensors.py @@ -306,7 +306,7 @@ def is_symmetric(self, tol: float = 1e-5) -> bool: Args: tol (float): tolerance to test for symmetry """ - return (self - self.symmetrized < tol).all() + return np.allclose(self, self.symmetrized, atol=tol, rtol=0) def fit_to_structure( self, @@ -336,7 +336,7 @@ def is_fit_to_structure(self, structure: Structure, tol: float = 1e-2) -> bool: structure (Structure): structure to be fit to tol (float): tolerance for symmetry testing """ - return bool((self - self.fit_to_structure(structure) < tol).all()) + return np.allclose(self, self.fit_to_structure(structure), atol=tol, rtol=0) @property def voigt(self) -> NDArray: @@ -973,7 +973,7 @@ def is_rotation( det = np.abs(np.linalg.det(self)) if include_improper: det = np.abs(det) - return bool((np.abs(self.inv - self.trans) < tol).all() and (np.abs(det - 1.0) < tol)) + return np.allclose(self.inv, self.trans, atol=tol, rtol=0) and np.allclose(det, 1.0, atol=tol, rtol=0) def refine_rotation(self) -> Self: """Helper method for refining rotation matrix by ensuring diff --git a/src/pymatgen/electronic_structure/bandstructure.py b/src/pymatgen/electronic_structure/bandstructure.py index b25bb1deaa5..a9a1853398f 100644 --- a/src/pymatgen/electronic_structure/bandstructure.py +++ b/src/pymatgen/electronic_structure/bandstructure.py @@ -356,7 +356,7 @@ def get_vbm(self) -> dict[str, Any]: list_ind_band = defaultdict(list) for spin in self.bands: for idx in range(self.nb_bands): - if math.fabs(self.bands[spin][idx][index] - max_tmp) < 0.001: + if math.isclose(self.bands[spin][idx][index], max_tmp, abs_tol=1e-3, rel_tol=0): list_ind_band[spin].append(idx) proj = {} for spin, value in self.projections.items(): @@ -422,7 +422,7 @@ def get_cbm(self) -> dict[str, Any]: list_index_band = defaultdict(list) for spin in self.bands: for idx in range(self.nb_bands): - if math.fabs(self.bands[spin][idx][index] - max_tmp) < 0.001: + if math.isclose(self.bands[spin][idx][index], max_tmp, abs_tol=1e-3, rel_tol=0): list_index_band[spin].append(idx) proj = {} for spin, value in self.projections.items(): diff --git a/src/pymatgen/electronic_structure/boltztrap.py b/src/pymatgen/electronic_structure/boltztrap.py index 476277a1bc6..7f1c484c5d4 100644 --- a/src/pymatgen/electronic_structure/boltztrap.py +++ b/src/pymatgen/electronic_structure/boltztrap.py @@ -425,7 +425,7 @@ def write_intrans(self, output_file) -> None: Args: output_file: Filename """ - set_gap = 1 if self.scissor > 0.0001 else 0 + set_gap = 1 if self.scissor > 1e-4 else 0 if self.run_type in ("BOLTZ", "DOS"): with open(output_file, mode="w") as fout: diff --git a/src/pymatgen/io/abinit/abiobjects.py b/src/pymatgen/io/abinit/abiobjects.py index e2ef5b83dad..87858e761c5 100644 --- a/src/pymatgen/io/abinit/abiobjects.py +++ b/src/pymatgen/io/abinit/abiobjects.py @@ -966,7 +966,7 @@ def automatic_density( mult = (ngrid * lengths[0] * lengths[1] * lengths[2]) ** (1 / 3.0) - num_div = [int(round(1.0 / lengths[i] * mult)) for i in range(3)] + num_div = [round(1.0 / lengths[i] * mult) for i in range(3)] # ensure that num_div[i] > 0 num_div = [i if i > 0 else 1 for i in num_div] diff --git a/src/pymatgen/io/abinit/inputs.py b/src/pymatgen/io/abinit/inputs.py index b279fd5aded..a01b9517cf5 100644 --- a/src/pymatgen/io/abinit/inputs.py +++ b/src/pymatgen/io/abinit/inputs.py @@ -98,9 +98,9 @@ class T(NamedTuple): _tolerances = { - "toldfe": T(1.0e-7, 1.0e-8, 1.0e-9), - "tolvrs": T(1.0e-7, 1.0e-8, 1.0e-9), - "tolwfr": T(1.0e-15, 1.0e-17, 1.0e-19), + "toldfe": T(1e-7, 1e-8, 1e-9), + "tolvrs": T(1e-7, 1e-8, 1e-9), + "tolwfr": T(1e-15, 1e-17, 1e-19), "tolrff": T(0.04, 0.02, 0.01), } del T diff --git a/src/pymatgen/io/cif.py b/src/pymatgen/io/cif.py index 8606216a683..1e67f3fcea8 100644 --- a/src/pymatgen/io/cif.py +++ b/src/pymatgen/io/cif.py @@ -583,7 +583,7 @@ def _sanitize_data(self, data: CifBlock) -> CifBlock: continue for comparison_frac in important_fracs: - if abs(1 - frac / comparison_frac) < self._frac_tolerance: + if math.isclose(frac / comparison_frac, 1, abs_tol=self._frac_tolerance, rel_tol=0): fracs_to_change[label, idx] = str(comparison_frac) if fracs_to_change: diff --git a/src/pymatgen/io/feff/sets.py b/src/pymatgen/io/feff/sets.py index 72e61f8fe36..e1605fff5ca 100644 --- a/src/pymatgen/io/feff/sets.py +++ b/src/pymatgen/io/feff/sets.py @@ -10,6 +10,7 @@ import abc import logging +import math import os import sys import warnings @@ -250,7 +251,7 @@ def tags(self) -> Tags: if not self.config_dict.get("KMESH"): abc = self.structure.lattice.abc mult = (self.nkpts * abc[0] * abc[1] * abc[2]) ** (1 / 3) - self.config_dict["KMESH"] = [int(round(mult / length)) for length in abc] + self.config_dict["KMESH"] = [round(mult / length) for length in abc] else: logger.warning("Large system(>=14 atoms) or EXAFS calculation, removing K-space settings") del self.config_dict["RECIPROCAL"] @@ -314,14 +315,10 @@ def from_directory(cls, input_dir: str) -> Self: distance_matrix = input_atoms.distance_matrix[0, :] # Get radius value - from math import ceil - - radius = int( - ceil( - input_atoms.get_distance( - input_atoms.index(input_atoms[0]), - input_atoms.index(input_atoms[-1]), - ) + radius = math.ceil( + input_atoms.get_distance( + input_atoms.index(input_atoms[0]), + input_atoms.index(input_atoms[-1]), ) ) diff --git a/src/pymatgen/io/icet.py b/src/pymatgen/io/icet.py index 769952ca445..2cd40bac345 100644 --- a/src/pymatgen/io/icet.py +++ b/src/pymatgen/io/icet.py @@ -49,7 +49,7 @@ class IcetSQS: } _sqs_kwarg_defaults: ClassVar[dict[str, Any]] = { "optimality_weight": None, - "tol": 1.0e-5, + "tol": 1e-5, "include_smaller_cells": False, # for consistency with ATAT "pbc": (True, True, True), } diff --git a/src/pymatgen/io/lobster/lobsterenv.py b/src/pymatgen/io/lobster/lobsterenv.py index be5622e92cf..81b600452ec 100644 --- a/src/pymatgen/io/lobster/lobsterenv.py +++ b/src/pymatgen/io/lobster/lobsterenv.py @@ -820,7 +820,7 @@ def _evaluate_ce( { "site": neighbor, "image": tuple( - int(round(idx)) + round(idx) for idx in ( neighbor.frac_coords - self.structure[ @@ -861,7 +861,7 @@ def _evaluate_ce( { "site": neighbor, "image": tuple( - int(round(idx)) + round(idx) for idx in ( neighbor.frac_coords - self.structure[ diff --git a/src/pymatgen/io/openff.py b/src/pymatgen/io/openff.py index 0cdb4e0c28b..5294f668d19 100644 --- a/src/pymatgen/io/openff.py +++ b/src/pymatgen/io/openff.py @@ -48,7 +48,7 @@ def mol_graph_to_openff_mol(mol_graph: MoleculeGraph) -> tk.Molecule: # put formal charge on first atom if there is none present formal_charge = node.get("formal_charge") if formal_charge is None: - formal_charge = (i_node == 0) * int(round(mol_graph.molecule.charge, 0)) * unit.elementary_charge + formal_charge = (i_node == 0) * round(mol_graph.molecule.charge) * unit.elementary_charge # assume not aromatic if no info present is_aromatic = node.get("is_aromatic") or False diff --git a/src/pymatgen/io/qchem/outputs.py b/src/pymatgen/io/qchem/outputs.py index fbf72e7f7cb..fb359464e98 100644 --- a/src/pymatgen/io/qchem/outputs.py +++ b/src/pymatgen/io/qchem/outputs.py @@ -1422,7 +1422,7 @@ def _read_gradients(self): if len(parsed_gradients) >= 1: sorted_gradients = np.zeros(shape=(len(parsed_gradients), len(self.data["initial_molecule"]), 3)) for ii, grad in enumerate(parsed_gradients): - for jj in range(int(len(grad) / 3)): + for jj in range(len(grad) // 3): for kk in range(grad_format_length): if grad[jj * 3][kk] != "None": sorted_gradients[ii][jj * grad_format_length + kk][0] = grad[jj * 3][kk] @@ -1457,7 +1457,7 @@ def _read_gradients(self): sorted_gradients = np.zeros(shape=(len(parsed_gradients), len(self.data["initial_molecule"]), 3)) for ii, grad in enumerate(parsed_gradients): - for jj in range(int(len(grad) / 3)): + for jj in range(len(grad) // 3): for kk in range(grad_format_length): if grad[jj * 3][kk] != "None": sorted_gradients[ii][jj * grad_format_length + kk][0] = grad[jj * 3][kk] diff --git a/src/pymatgen/io/vasp/inputs.py b/src/pymatgen/io/vasp/inputs.py index 91eab02b099..1ec54342217 100644 --- a/src/pymatgen/io/vasp/inputs.py +++ b/src/pymatgen/io/vasp/inputs.py @@ -1414,7 +1414,7 @@ def automatic_density( if comment is None: comment = f"pymatgen with grid density = {kppa:.0f} / number of atoms" - if math.fabs((math.floor(kppa ** (1 / 3) + 0.5)) ** 3 - kppa) < 1: + if abs((math.floor(kppa ** (1 / 3) + 0.5)) ** 3 - kppa) < 1: kppa += kppa * 0.01 lattice = structure.lattice lengths: Vector3D = lattice.abc diff --git a/src/pymatgen/io/vasp/sets.py b/src/pymatgen/io/vasp/sets.py index 4b59d170389..18c5539623b 100644 --- a/src/pymatgen/io/vasp/sets.py +++ b/src/pymatgen/io/vasp/sets.py @@ -671,7 +671,7 @@ def incar(self) -> Incar: "better off changing the values of MAGMOM or simply setting " "NUPDOWN directly in your INCAR settings.", UserWarning, - stacklevel=1, + stacklevel=2, ) auto_updates["NUPDOWN"] = nupdown @@ -2009,7 +2009,7 @@ def incar_updates(self) -> dict[str, Any]: if self.mode.lower() == "cs": updates.update( LCHIMAG=True, - EDIFF=-1.0e-10, + EDIFF=-1e-10, ISYM=0, LCHARG=False, LNMR_SYM_RED=True, @@ -2026,7 +2026,7 @@ def incar_updates(self) -> dict[str, Any]: ] updates.update( ALGO="FAST", - EDIFF=-1.0e-10, + EDIFF=-1e-10, ISYM=0, LCHARG=False, LEFG=True, @@ -3173,7 +3173,7 @@ def incar_updates(self) -> dict[str, Any]: """Updates to the INCAR config for this calculation type.""" updates = { "ALGO": "Exact", - "EDIFF": 1.0e-8, + "EDIFF": 1e-8, "IBRION": -1, "ICHARG": 1, "ISMEAR": 0, diff --git a/src/pymatgen/io/xr.py b/src/pymatgen/io/xr.py index 4ad17556412..834b8d6d797 100644 --- a/src/pymatgen/io/xr.py +++ b/src/pymatgen/io/xr.py @@ -10,7 +10,6 @@ from __future__ import annotations import re -from math import fabs from typing import TYPE_CHECKING import numpy as np @@ -73,7 +72,7 @@ def write_file(self, filename: str | Path) -> None: file.write(str(self) + "\n") @classmethod - def from_str(cls, string: str, use_cores: bool = True, thresh: float = 1.0e-4) -> Self: + def from_str(cls, string: str, use_cores: bool = True, thresh: float = 1e-4) -> Self: """ Creates an Xr object from a string representation. @@ -107,12 +106,12 @@ def from_str(cls, string: str, use_cores: bool = True, thresh: float = 1.0e-4) - mat[i] = np.array([float(w) for w in tokens]) lattice = Lattice(mat) if ( - fabs(lattice.a - lengths[0]) / fabs(lattice.a) > thresh - or fabs(lattice.b - lengths[1]) / fabs(lattice.b) > thresh - or fabs(lattice.c - lengths[2]) / fabs(lattice.c) > thresh - or fabs(lattice.alpha - angles[0]) / fabs(lattice.alpha) > thresh - or fabs(lattice.beta - angles[1]) / fabs(lattice.beta) > thresh - or fabs(lattice.gamma - angles[2]) / fabs(lattice.gamma) > thresh + abs(lattice.a - lengths[0]) / abs(lattice.a) > thresh + or abs(lattice.b - lengths[1]) / abs(lattice.b) > thresh + or abs(lattice.c - lengths[2]) / abs(lattice.c) > thresh + or abs(lattice.alpha - angles[0]) / abs(lattice.alpha) > thresh + or abs(lattice.beta - angles[1]) / abs(lattice.beta) > thresh + or abs(lattice.gamma - angles[2]) / abs(lattice.gamma) > thresh ): raise RuntimeError( f"cell parameters in header ({lengths}, {angles}) are not consistent with Cartesian " @@ -139,7 +138,7 @@ def from_str(cls, string: str, use_cores: bool = True, thresh: float = 1.0e-4) - return cls(Structure(lattice, sp, coords, coords_are_cartesian=True)) @classmethod - def from_file(cls, filename: str | Path, use_cores: bool = True, thresh: float = 1.0e-4) -> Self: + def from_file(cls, filename: str | Path, use_cores: bool = True, thresh: float = 1e-4) -> Self: """ Reads an xr-formatted file to create an Xr object. diff --git a/src/pymatgen/symmetry/analyzer.py b/src/pymatgen/symmetry/analyzer.py index ad8bfdf5ebd..db2687ccf73 100644 --- a/src/pymatgen/symmetry/analyzer.py +++ b/src/pymatgen/symmetry/analyzer.py @@ -503,7 +503,7 @@ def get_conventional_to_primitive_transformation_matrix( # Check if the conventional representation is hexagonal or # rhombohedral lengths = conv.lattice.lengths - if abs(lengths[0] - lengths[2]) < 0.0001: + if abs(lengths[0] - lengths[2]) < 1e-4: return np.eye return np.array([[-1, 1, 1], [2, 1, 1], [-1, -2, 1]], dtype=np.float64) / 3 diff --git a/src/pymatgen/symmetry/bandstructure.py b/src/pymatgen/symmetry/bandstructure.py index 392dbf63a74..c30fdec3aaa 100644 --- a/src/pymatgen/symmetry/bandstructure.py +++ b/src/pymatgen/symmetry/bandstructure.py @@ -306,7 +306,7 @@ def get_continuous_path(bandstructure): labels = [point.label for point in bandstructure.kpoints if point.label is not None] plot_axis = [] - for i in range(int(len(labels) / 2)): + for i in range(len(labels) // 2): G.add_edges_from([(labels[2 * i], labels[(2 * i) + 1])]) plot_axis.append((labels[2 * i], labels[(2 * i) + 1])) diff --git a/src/pymatgen/symmetry/kpath.py b/src/pymatgen/symmetry/kpath.py index 93ff35991e7..3a7fd573013 100644 --- a/src/pymatgen/symmetry/kpath.py +++ b/src/pymatgen/symmetry/kpath.py @@ -4,7 +4,8 @@ import abc import itertools -from math import ceil, cos, e, pi, sin, tan +import math +from math import cos, pi, sin from typing import TYPE_CHECKING from warnings import warn @@ -102,7 +103,7 @@ def get_kpoints(self, line_density=20, coords_are_cartesian=True): distance = np.linalg.norm( self._rec_lattice.get_cartesian_coords(start) - self._rec_lattice.get_cartesian_coords(end) ) - nb = ceil(distance * line_density) + nb = math.ceil(distance * line_density) if nb == 0: continue sym_point_labels.extend([k_path[path_step - 1]] + [""] * (nb - 1) + [k_path[path_step]]) @@ -603,7 +604,7 @@ def rhl1(self, alpha): def rhl2(self, alpha): """RHL2 Path.""" self.name = "RHL2" - eta = 1 / (2 * tan(alpha / 2.0) ** 2) + eta = 1 / (2 * math.tan(alpha / 2.0) ** 2) nu = 3 / 4 - eta / 2.0 kpoints = { "\\Gamma": np.array([0.0, 0.0, 0.0]), @@ -1387,7 +1388,7 @@ def _choose_path( return point_orbits_in_path, line_orbits_in_path def _get_key_points(self): - decimals = ceil(-1 * np.log10(self._atol)) - 1 + decimals = math.ceil(-1 * np.log10(self._atol)) - 1 bz = self._rec_lattice.get_wigner_seitz_cell() key_points = [] @@ -1537,7 +1538,7 @@ def _get_key_line_orbits(self, key_points, key_lines, key_points_inds_orbits): to_pop = [] p00 = key_points[l0[0]] p01 = key_points[l0[1]] - pmid0 = p00 + e / pi * (p01 - p00) + pmid0 = p00 + math.e / pi * (p01 - p00) for ind_key, l1 in key_lines_copy.items(): p10 = key_points[l1[0]] p11 = key_points[l1[1]] @@ -1555,7 +1556,7 @@ def _get_key_line_orbits(self, key_points, key_lines, key_points_inds_orbits): equivptsperp = True if equivptspar: - pmid1 = p10 + e / pi * (p11 - p10) + pmid1 = p10 + math.e / pi * (p11 - p10) for op in self._rpg: if not equivline: p00pr = np.dot(op, p00) @@ -1569,7 +1570,7 @@ def _get_key_line_orbits(self, key_points, key_lines, key_points_inds_orbits): equivline = True elif equivptsperp: - pmid1 = p11 + e / pi * (p10 - p11) + pmid1 = p11 + math.e / pi * (p10 - p11) for op in self._rpg: if not equivline: p00pr = np.dot(op, p00) @@ -1739,7 +1740,9 @@ def _closewrapped(pos1, pos2, tolerance): if len(pos1) != len(pos2): return False for idx, p1 in enumerate(pos1): - if abs(p1 - pos2[idx]) > tolerance[idx] and abs(p1 - pos2[idx]) < 1 - tolerance[idx]: + if not math.isclose(p1, pos2[idx], abs_tol=tolerance[idx], rel_tol=0) and math.isclose( + p1, pos2[idx], abs_tol=1 - tolerance[idx], rel_tol=0 + ): return False return True diff --git a/src/pymatgen/transformations/advanced_transformations.py b/src/pymatgen/transformations/advanced_transformations.py index cdc89a11979..afda9fb7f8d 100644 --- a/src/pymatgen/transformations/advanced_transformations.py +++ b/src/pymatgen/transformations/advanced_transformations.py @@ -7,7 +7,6 @@ import warnings from fractions import Fraction from itertools import groupby, product -from math import gcd from string import ascii_lowercase from typing import TYPE_CHECKING @@ -425,7 +424,7 @@ def _get_stats(struct): if contains_oxidation_state and self.sort_criteria == "ewald": new_latt = struct.lattice transformation = np.dot(new_latt.matrix, inv_latt) - transformation = tuple(tuple(int(round(cell)) for cell in row) for row in transformation) + transformation = tuple(tuple(round(cell) for cell in row) for row in transformation) if transformation not in ewald_matrices: s_supercell = structure * transformation ewald = EwaldSummation(s_supercell) @@ -660,7 +659,7 @@ def determine_min_cell(disordered_structure): def lcm(n1, n2): """Find least common multiple of two numbers.""" - return n1 * n2 / gcd(n1, n2) + return n1 * n2 / math.gcd(n1, n2) # assumes all order parameters for a given species are the same mag_species_order_parameter = {} @@ -683,7 +682,7 @@ def lcm(n1, n2): for sp, order_parameter in mag_species_order_parameter.items(): denom = Fraction(order_parameter).limit_denominator(100).denominator num_atom_per_specie = mag_species_occurrences[sp] - n_gcd = gcd(denom, num_atom_per_specie) + n_gcd = math.gcd(denom, num_atom_per_specie) smallest_n.append(lcm(int(n_gcd), denom) / n_gcd) return max(smallest_n) @@ -1030,7 +1029,7 @@ def apply_transformation(self, structure: Structure, return_ranked_list: bool | logger.info(f"Compatible species: {compatible_species}") lengths = structure.lattice.abc - scaling = [max(1, int(round(math.ceil(self.min_length / x)))) for x in lengths] + scaling = [max(1, math.ceil(self.min_length / x)) for x in lengths] logger.info(f"{lengths=}") logger.info(f"{scaling=}") @@ -1333,7 +1332,7 @@ def __init__( ratio=True, plane=None, max_search=20, - tol_coi=1.0e-8, + tol_coi=1e-8, rm_ratio=0.7, quick_gen=False, ): diff --git a/src/pymatgen/transformations/site_transformations.py b/src/pymatgen/transformations/site_transformations.py index f188a6f5fad..4672bad1183 100644 --- a/src/pymatgen/transformations/site_transformations.py +++ b/src/pymatgen/transformations/site_transformations.py @@ -426,9 +426,9 @@ def apply_transformation(self, structure: Structure, return_ranked_list: bool | total_combos = 0 for idx, frac in zip(self.indices, self.fractions, strict=True): n_to_remove = len(idx) * frac - if abs(n_to_remove - int(round(n_to_remove))) > 1e-3: + if abs(n_to_remove - round(n_to_remove)) > 1e-3: raise ValueError("Fraction to remove must be consistent with integer amounts in structure.") - n_to_remove = int(round(n_to_remove)) + n_to_remove = round(n_to_remove) num_remove_dict[tuple(idx)] = n_to_remove n = len(idx) total_combos += int( diff --git a/src/pymatgen/transformations/standard_transformations.py b/src/pymatgen/transformations/standard_transformations.py index 634b74840c6..8d8e231b8ca 100644 --- a/src/pymatgen/transformations/standard_transformations.py +++ b/src/pymatgen/transformations/standard_transformations.py @@ -10,7 +10,6 @@ from typing import TYPE_CHECKING import numpy as np -from numpy import around from pymatgen.analysis.bond_valence import BVAnalyzer from pymatgen.analysis.elasticity.strain import Deformation @@ -539,7 +538,7 @@ def apply_transformation(self, structure: Structure, return_ranked_list: bool | for key, val in total_occupancy.items(): if abs(val - round(val)) > 0.25: raise ValueError("Occupancy fractions not consistent with size of unit cell") - total_occupancy[key] = int(round(val)) + total_occupancy[key] = round(val) # start with an ordered structure initial_sp = max(total_occupancy, key=lambda x: abs(x.oxi_state)) for idx in group: @@ -783,7 +782,7 @@ def apply_transformation(self, structure) -> Structure: old_occ = sp[k] new_occ = float(Fraction(old_occ).limit_denominator(self.max_denominator)) if self.fix_denominator: - new_occ = around(old_occ * self.max_denominator) / self.max_denominator + new_occ = np.around(old_occ * self.max_denominator) / self.max_denominator if round(abs(old_occ - new_occ), 6) > self.tol: raise RuntimeError("Cannot discretize structure within tolerance!") sp[k] = new_occ diff --git a/src/pymatgen/util/coord.py b/src/pymatgen/util/coord.py index 59d7c48d10f..f9db8424a87 100644 --- a/src/pymatgen/util/coord.py +++ b/src/pymatgen/util/coord.py @@ -456,7 +456,7 @@ def line_intersection(self, point1: Sequence[float], point2: Sequence[float], to found = False # don't return duplicate points for b in barys: - if np.all(np.abs(b - p) < tolerance): + if np.allclose(b, p, atol=tolerance, rtol=0): found = True break if not found: diff --git a/tests/analysis/chemenv/coordination_environments/test_weights.py b/tests/analysis/chemenv/coordination_environments/test_weights.py index 31c252c3782..6a5b2ffe50a 100644 --- a/tests/analysis/chemenv/coordination_environments/test_weights.py +++ b/tests/analysis/chemenv/coordination_environments/test_weights.py @@ -68,10 +68,10 @@ def test_angle_weight(self): fake_nb_set.angles = [] angle_weight = AngleNbSetWeight(aa=1) aw = angle_weight.weight(nb_set=fake_nb_set, structure_environments=dummy_se) - assert abs(aw - 0) < 1e-8 + assert abs(aw) < 1e-8 angle_weight = AngleNbSetWeight(aa=2) aw = angle_weight.weight(nb_set=fake_nb_set, structure_environments=dummy_se) - assert abs(aw - 0) < 1e-8 + assert abs(aw) < 1e-8 # nb_set with one neighbor fake_nb_set.angles = [3.08570351705799] @@ -289,7 +289,7 @@ def test_self_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(self_w - 0) < 1e-8 + assert abs(self_w) < 1e-8 cn_map = (12, 0) self_w = self_weight.weight( nb_set=nb_sets[cn_map], @@ -297,7 +297,7 @@ def test_self_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(self_w - 0) < 1e-8 + assert abs(self_w) < 1e-8 cn_map = (12, 1) self_w = self_weight.weight( nb_set=nb_sets[cn_map], @@ -305,7 +305,7 @@ def test_self_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(self_w - 0) < 1e-8 + assert abs(self_w) < 1e-8 cn_map = (13, 2) self_w = self_weight.weight( nb_set=nb_sets[cn_map], @@ -391,7 +391,7 @@ def test_delta_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(delta_w - 0) < 1e-8 + assert abs(delta_w) < 1e-8 cn_map = (12, 2) delta_w = delta_weight.weight( nb_set=nb_sets[cn_map], @@ -399,7 +399,7 @@ def test_delta_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(delta_w - 0) < 1e-8 + assert abs(delta_w) < 1e-8 cn_map = (12, 0) delta_w = delta_weight.weight( nb_set=nb_sets[cn_map], @@ -407,7 +407,7 @@ def test_delta_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(delta_w - 0) < 1e-8 + assert abs(delta_w) < 1e-8 cn_map = (12, 1) delta_w = delta_weight.weight( nb_set=nb_sets[cn_map], @@ -415,7 +415,7 @@ def test_delta_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(delta_w - 0) < 1e-8 + assert abs(delta_w) < 1e-8 cn_map = (13, 2) delta_w = delta_weight.weight( nb_set=nb_sets[cn_map], @@ -431,7 +431,7 @@ def test_delta_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(delta_w - 0) < 1e-8 + assert abs(delta_w) < 1e-8 cn_map = (13, 1) delta_w = delta_weight.weight( nb_set=nb_sets[cn_map], @@ -439,7 +439,7 @@ def test_delta_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(delta_w - 0) < 1e-8 + assert abs(delta_w) < 1e-8 effective_csm_estimator = { "function": "power2_inverse_decreasing", @@ -524,7 +524,7 @@ def test_delta_csms_weight(self): cn_map=cn_map, additional_info=additional_info, ) - assert abs(delta_w - 0) < 1e-8 + assert abs(delta_w) < 1e-8 cn_map = (4, 0) delta_w = delta_weight.weight( nb_set=nb_sets[cn_map], @@ -661,7 +661,7 @@ def test_dist_nb_set_weight(self): cn_map7 = (7, 0) weight1 = dnbset_weight.weight(fake_nb_set1, dummy_se, cn_map=cn_map1, additional_info=None) - assert abs(weight1 - 0) < 1e-8 + assert abs(weight1) < 1e-8 weight2 = dnbset_weight.weight(fake_nb_set2, dummy_se, cn_map=cn_map2, additional_info=None) assert abs(weight2 - 0.103515625) < 1e-8 weight3 = dnbset_weight.weight(fake_nb_set3, dummy_se, cn_map=cn_map3, additional_info=None) @@ -712,9 +712,9 @@ def test_dist_nb_set_weight(self): weight_delta1 = delta_dnb_set_weight.weight(fake_nb_set1, dummy_se, cn_map=cn_map1, additional_info=None) assert abs(weight_delta1 - 1) < 1e-8 weight_delta2 = delta_dnb_set_weight.weight(fake_nb_set2, dummy_se, cn_map=cn_map2, additional_info=None) - assert abs(weight_delta2 - 0) < 1e-8 + assert abs(weight_delta2) < 1e-8 weight_delta3 = delta_dnb_set_weight.weight(fake_nb_set3, dummy_se, cn_map=cn_map3, additional_info=None) - assert abs(weight_delta3 - 0) < 1e-8 + assert abs(weight_delta3) < 1e-8 delta_dnb_set_weight2 = DeltaDistanceNbSetWeight( weight_function={ @@ -726,9 +726,9 @@ def test_dist_nb_set_weight(self): weight_delta1 = delta_dnb_set_weight2.weight(fake_nb_set1, dummy_se, cn_map=cn_map1, additional_info=None) assert abs(weight_delta1 - 0.5) < 1e-8 weight_delta2 = delta_dnb_set_weight2.weight(fake_nb_set2, dummy_se, cn_map=cn_map2, additional_info=None) - assert abs(weight_delta2 - 0) < 1e-8 + assert abs(weight_delta2) < 1e-8 weight_delta3 = delta_dnb_set_weight2.weight(fake_nb_set3, dummy_se, cn_map=cn_map3, additional_info=None) - assert abs(weight_delta3 - 0) < 1e-8 + assert abs(weight_delta3) < 1e-8 delta_dnb_set_weight3 = DeltaDistanceNbSetWeight( weight_function={ diff --git a/tests/analysis/test_local_env.py b/tests/analysis/test_local_env.py index 29c5503c64a..5b79de83ee7 100644 --- a/tests/analysis/test_local_env.py +++ b/tests/analysis/test_local_env.py @@ -1,6 +1,5 @@ from __future__ import annotations -from math import pi from shutil import which from typing import get_args @@ -113,7 +112,7 @@ def test_solid_angle(self): for nn in self.nn.get_voronoi_polyhedra(self.struct, n).values(): angle += nn["solid_angle"] assert 4 * np.pi == approx(angle) - assert solid_angle([0, 0, 0], [[1, 0, 0], [-1, 0, 0], [0, 1, 0]]) == pi + assert solid_angle([0, 0, 0], [[1, 0, 0], [-1, 0, 0], [0, 1, 0]]) == np.pi def test_nn_shell(self): # First, make a SC lattice. Make my math easier diff --git a/tests/analysis/test_reaction_calculator.py b/tests/analysis/test_reaction_calculator.py index dea6c9f6322..82fc1e03bea 100644 --- a/tests/analysis/test_reaction_calculator.py +++ b/tests/analysis/test_reaction_calculator.py @@ -1,7 +1,7 @@ from __future__ import annotations +import math from collections import defaultdict -from math import isnan from unittest import TestCase import numpy as np @@ -508,7 +508,7 @@ def test_calculated_reaction_energy_uncertainty_for_nan(self): prods = list(filter(lambda e: e.reduced_formula == "Li2O2", entries)) rxn_with_uncertainty = ComputedReaction(reactants, prods) - assert isnan(rxn_with_uncertainty.calculated_reaction_energy_uncertainty) + assert math.isnan(rxn_with_uncertainty.calculated_reaction_energy_uncertainty) def test_init(self): assert str(self.rxn) == "2 Li + O2 -> Li2O2" diff --git a/tests/core/test_bonds.py b/tests/core/test_bonds.py index 777d938619a..fb75a2b11e2 100644 --- a/tests/core/test_bonds.py +++ b/tests/core/test_bonds.py @@ -68,8 +68,8 @@ def test_get_bond_order(self): assert approx(get_bond_order("C", "C", 1.34) - 2) == 0 assert approx(get_bond_order("C", "C", 1.4) - 1.7) == 0 # bond length in benzene assert approx(get_bond_order("C", "C", 1.54) - 1) == 0 - assert approx(get_bond_order("C", "C", 2.5) - 0) == 0 - assert approx(get_bond_order("C", "C", 9999) - 0) == 0 + assert approx(get_bond_order("C", "C", 2.5)) == 0 + assert approx(get_bond_order("C", "C", 9999)) == 0 assert approx(get_bond_order("C", "Br", 1.9, default_bl=1.9) - 1) == 0 assert approx(get_bond_order("C", "Br", 2, default_bl=1.9) - 0.7368421052631575) == 0 assert approx(get_bond_order("C", "Br", 1.9, tol=0.5, default_bl=1.9) - 1) == 0 diff --git a/tests/electronic_structure/test_dos.py b/tests/electronic_structure/test_dos.py index d5c891ed189..a7c8dec41b1 100644 --- a/tests/electronic_structure/test_dos.py +++ b/tests/electronic_structure/test_dos.py @@ -130,9 +130,9 @@ def test_get_gap(self): sum_element += pdos # The sums of the SPD or the element doses should be the same. - assert (abs(sum_spd.energies - sum_element.energies) < 0.0001).all() - assert (abs(sum_spd.densities[Spin.up] - sum_element.densities[Spin.up]) < 0.0001).all() - assert (abs(sum_spd.densities[Spin.down] - sum_element.densities[Spin.down]) < 0.0001).all() + assert_allclose(sum_spd.energies, sum_element.energies, atol=1e-4) + assert_allclose(sum_spd.densities[Spin.up], sum_element.densities[Spin.up], atol=1e-4) + assert_allclose(sum_spd.densities[Spin.down], sum_element.densities[Spin.down], atol=1e-4) site = self.dos.structure[0] assert self.dos.get_site_dos(site) is not None @@ -170,7 +170,7 @@ def test_as_from_dict(self): sum_element += pdos # The sums of the SPD or the element doses should be the same. - assert (abs(sum_spd.energies - sum_element.energies) < 0.0001).all() + assert_allclose(sum_spd.energies, sum_element.energies, atol=1e-4) def test_str(self): assert str(self.dos).startswith("Complete DOS for Full Formula (Li1 Fe4 P4 O16)\nReduced Formula: LiFe4(PO4)4") diff --git a/tests/entries/test_compatibility.py b/tests/entries/test_compatibility.py index 1d71c95388f..2aaa4cb02ce 100644 --- a/tests/entries/test_compatibility.py +++ b/tests/entries/test_compatibility.py @@ -2,9 +2,9 @@ import copy import json +import math import os from collections import defaultdict -from math import sqrt from pathlib import Path from typing import TYPE_CHECKING from unittest import TestCase @@ -2485,10 +2485,10 @@ def setUp(self): def test_errors(self): for entry, expected in ( - (self.entry1, sqrt((2 * 0.0101) ** 2 + (3 * 0.002) ** 2)), - (self.entry2, sqrt((3 * 0.0101) ** 2 + (4 * 0.002) ** 2)), + (self.entry1, math.sqrt((2 * 0.0101) ** 2 + (3 * 0.002) ** 2)), + (self.entry2, math.sqrt((3 * 0.0101) ** 2 + (4 * 0.002) ** 2)), (self.entry_sulfide, 0.0093), - (self.entry_fluoride, sqrt((3 * 0.0026) ** 2 + 0.0101**2)), + (self.entry_fluoride, math.sqrt((3 * 0.0026) ** 2 + 0.0101**2)), (self.entry_hydride, 0.0013), ): corrected_entry = self.compat.process_entry(entry) diff --git a/tests/io/qchem/test_utils.py b/tests/io/qchem/test_utils.py index 0c4681c4f21..4ad091b8242 100644 --- a/tests/io/qchem/test_utils.py +++ b/tests/io/qchem/test_utils.py @@ -34,7 +34,7 @@ def test_lower_and_check_unique(self): def test_process_parsed_hess(self): with zopen(f"{TEST_DIR}/parse_hess/132.0", mode="rb") as file: binary = file.read() - data_132 = [struct.unpack("d", binary[ii * 8 : (ii + 1) * 8])[0] for ii in range(int(len(binary) / 8))] + data_132 = [struct.unpack("d", binary[ii * 8 : (ii + 1) * 8])[0] for ii in range(len(binary) // 8)] with zopen(f"{TEST_DIR}/parse_hess/HESS", mode="rt", encoding="ISO-8859-1") as file: data_hess = file.readlines() diff --git a/tests/io/vasp/test_outputs.py b/tests/io/vasp/test_outputs.py index 46eb0fa60e7..e02b9ac027a 100644 --- a/tests/io/vasp/test_outputs.py +++ b/tests/io/vasp/test_outputs.py @@ -304,7 +304,7 @@ def test_standard(self): # Test with ionic_step_offset vasprun_offset = Vasprun(filepath, 3, 6, parse_potcar_file=False) - assert len(vasprun_offset.ionic_steps) == int(len(vasp_run.ionic_steps) / 3) - 1 + assert len(vasprun_offset.ionic_steps) == len(vasp_run.ionic_steps) // 3 - 1 assert vasprun_offset.structures[0] == vasprun_skip.structures[2] assert vasprun_ggau.is_hubbard diff --git a/tests/transformations/test_standard_transformations.py b/tests/transformations/test_standard_transformations.py index 42ee06704d0..0074eece962 100644 --- a/tests/transformations/test_standard_transformations.py +++ b/tests/transformations/test_standard_transformations.py @@ -9,6 +9,7 @@ import numpy as np import pytest from monty.json import MontyDecoder +from numpy.testing import assert_allclose from pytest import approx from pymatgen.core import Element, PeriodicSite @@ -59,7 +60,7 @@ def test_rotation_transformation(self): trafo = RotationTransformation([0, 1, 0], 30) s2 = trafo.apply_transformation(self.struct) s1 = trafo.inverse.apply_transformation(s2) - assert (abs(s1.lattice.matrix - self.struct.lattice.matrix) < 1e-8).all() + assert_allclose(s1.lattice.matrix, self.struct.lattice.matrix, atol=1e-8) class TestRemoveSpeciesTransformation: