Skip to content

Commit 2283493

Browse files
kavanaseshyuep
andauthored
Use np.nan instead of 0 for no uncertainty with ufloat, to avoid unnecessary warnings (#4400)
* Use `np.nan` instead of 0 for no uncertainty with `ufloat`, as recommended * Update rxn uncertainty handling and typing fix --------- Co-authored-by: Shyue Ping Ong <[email protected]>
1 parent 0197856 commit 2283493

File tree

4 files changed

+31
-20
lines changed

4 files changed

+31
-20
lines changed

src/pymatgen/analysis/reaction_calculator.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import numpy as np
1111
from monty.fractions import gcd_float
1212
from monty.json import MontyDecoder, MSONable
13-
from uncertainties import ufloat
13+
from uncertainties import UFloat, ufloat
1414

1515
from pymatgen.core.composition import Composition
1616
from pymatgen.entries.computed_entries import ComputedEntry
@@ -100,7 +100,7 @@ def __str__(self):
100100
__repr__ = __str__
101101

102102
@overload
103-
def calculate_energy(self, energies: dict[Composition, ufloat]) -> ufloat:
103+
def calculate_energy(self, energies: dict[Composition, ufloat]) -> UFloat:
104104
pass
105105

106106
@overload
@@ -485,10 +485,15 @@ def calculated_reaction_energy_uncertainty(self) -> float:
485485

486486
for entry in self._reactant_entries + self._product_entries:
487487
comp, factor = entry.composition.get_reduced_composition_and_factor()
488-
energy_ufloat = ufloat(entry.energy, entry.correction_uncertainty)
488+
energy_ufloat = (
489+
ufloat(entry.energy, entry.correction_uncertainty)
490+
if entry.correction_uncertainty and not np.isnan(entry.correction_uncertainty)
491+
else entry.energy
492+
)
489493
calc_energies[comp] = min(calc_energies.get(comp, float("inf")), energy_ufloat / factor)
490494

491-
return self.calculate_energy(calc_energies).std_dev
495+
ufloat_reaction_energy = self.calculate_energy(calc_energies)
496+
return ufloat_reaction_energy.std_dev if isinstance(ufloat_reaction_energy, UFloat) else np.nan
492497

493498
def as_dict(self) -> dict:
494499
"""

src/pymatgen/entries/compatibility.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,14 @@ def correct_entry(self, entry):
121121
"""
122122
new_corr = self.get_correction(entry)
123123
old_std_dev = entry.correction_uncertainty
124-
if np.isnan(old_std_dev):
125-
old_std_dev = 0
126-
old_corr = ufloat(entry.correction, old_std_dev)
124+
old_corr = ufloat(entry.correction, 0 if np.isnan(old_std_dev) else old_std_dev)
127125
updated_corr = new_corr + old_corr
128126

129127
# if there are no error values available for the corrections applied,
130128
# set correction uncertainty to not a number
131-
uncertainty = np.nan if updated_corr.nominal_value != 0 and updated_corr.std_dev == 0 else updated_corr.std_dev
132-
133-
entry.energy_adjustments.append(ConstantEnergyAdjustment(updated_corr.nominal_value, uncertainty))
129+
entry.energy_adjustments.append(
130+
ConstantEnergyAdjustment(updated_corr.nominal_value, updated_corr.std_dev or np.nan)
131+
)
134132

135133
return entry
136134

@@ -195,7 +193,7 @@ def get_correction(self, entry: AnyComputedEntry) -> ufloat:
195193
ufloat: 0.0 +/- 0.0 (from uncertainties package)
196194
"""
197195
if SETTINGS.get("PMG_POTCAR_CHECKS") is False or not self.check_potcar:
198-
return ufloat(0.0, 0.0)
196+
return ufloat(0.0, np.nan)
199197

200198
potcar_spec = entry.parameters.get("potcar_spec")
201199
if self.check_hash:
@@ -211,7 +209,7 @@ def get_correction(self, entry: AnyComputedEntry) -> ufloat:
211209
expected_psp = {self.valid_potcars.get(el.symbol) for el in entry.elements}
212210
if expected_psp != psp_settings:
213211
raise CompatibilityError(f"Incompatible POTCAR {psp_settings}, expected {expected_psp}")
214-
return ufloat(0.0, 0.0)
212+
return ufloat(0.0, np.nan)
215213

216214

217215
@cached_class
@@ -249,6 +247,8 @@ def get_correction(self, entry: ComputedEntry | ComputedStructureEntry) -> ufloa
249247
if rform in self.cpd_energies:
250248
correction += self.cpd_energies[rform] * comp.num_atoms - entry.uncorrected_energy
251249

250+
if correction.std_dev == 0:
251+
correction = ufloat(correction.nominal_value, np.nan) # set std_dev to nan if no uncertainty
252252
return correction
253253

254254

@@ -286,7 +286,7 @@ def get_correction(self, entry: ComputedEntry | ComputedStructureEntry) -> ufloa
286286
"""
287287
comp = entry.composition
288288
if len(comp) == 1: # Skip element entry
289-
return ufloat(0.0, 0.0)
289+
return ufloat(0.0, np.nan)
290290

291291
correction = ufloat(0.0, 0.0)
292292

@@ -345,6 +345,8 @@ def get_correction(self, entry: ComputedEntry | ComputedStructureEntry) -> ufloa
345345
else:
346346
correction += self.oxide_correction["oxide"] * comp["O"]
347347

348+
if correction.std_dev == 0:
349+
correction = ufloat(correction.nominal_value, np.nan) # set std_dev to nan if no uncertainty
348350
return correction
349351

350352

@@ -432,6 +434,8 @@ def get_correction(self, entry: ComputedEntry | ComputedStructureEntry) -> ufloa
432434
correction += ufloat(-1 * MU_H2O * nH2O, 0.0)
433435
# correction += 0.5 * 2.46 * nH2O # this is the old way this correction was calculated
434436

437+
if correction.std_dev == 0:
438+
correction = ufloat(correction.nominal_value, np.nan) # set std_dev to nan if no uncertainty
435439
return correction
436440

437441

@@ -537,6 +541,8 @@ def get_correction(self, entry: ComputedEntry | ComputedStructureEntry) -> ufloa
537541
if sym in u_corr:
538542
correction += ufloat(u_corr[sym], u_errors[sym]) * comp[el]
539543

544+
if correction.std_dev == 0:
545+
correction = ufloat(correction.nominal_value, np.nan) # set std_dev to nan if no uncertainty
540546
return correction
541547

542548

@@ -1076,7 +1082,7 @@ def get_adjustments(self, entry: AnyComputedEntry) -> list[CompositionEnergyAdju
10761082
)
10771083

10781084
# check the POTCAR symbols
1079-
# this should return ufloat(0, 0) or raise a CompatibilityError or ValueError
1085+
# this should return ufloat(0, np.nan) or raise a CompatibilityError or ValueError
10801086
if entry.parameters.get("software", "vasp") == "vasp":
10811087
pc = PotcarCorrection(
10821088
MPRelaxSet,

src/pymatgen/entries/computed_entries.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,8 @@ def correction(self) -> float:
363363
Returns:
364364
float: the total energy correction / adjustment applied to the entry in eV.
365365
"""
366-
# adds to ufloat(0.0, 0.0) to ensure that no corrections still result in ufloat object
367-
corr = ufloat(0.0, 0.0) + sum(ufloat(ea.value, ea.uncertainty) for ea in self.energy_adjustments)
366+
# either sum of adjustments or ufloat with nan std_dev, so that no corrections still result in ufloat object:
367+
corr = sum(ufloat(ea.value, ea.uncertainty) for ea in self.energy_adjustments) or ufloat(0.0, np.nan)
368368
return corr.nominal_value
369369

370370
@correction.setter
@@ -386,11 +386,11 @@ def correction_uncertainty(self) -> float:
386386
Returns:
387387
float: the uncertainty of the energy adjustments applied to the entry in eV.
388388
"""
389-
# adds to ufloat(0.0, 0.0) to ensure that no corrections still result in ufloat object
390-
unc = ufloat(0.0, 0.0) + sum(
389+
# either sum of adjustments or ufloat with nan std_dev, so that no corrections still result in ufloat object:
390+
unc = sum(
391391
(ufloat(ea.value, ea.uncertainty) if not np.isnan(ea.uncertainty) else ufloat(ea.value, 0))
392392
for ea in self.energy_adjustments
393-
)
393+
) or ufloat(0.0, np.nan)
394394

395395
if unc.nominal_value != 0 and unc.std_dev == 0:
396396
return np.nan

tests/analysis/test_reaction_calculator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ def test_calculated_reaction_energy_uncertainty(self):
441441
def test_calculated_reaction_energy_uncertainty_for_no_uncertainty(self):
442442
# test that reaction_energy_uncertainty property doesn't cause errors
443443
# when products/reactants have no uncertainties
444-
assert self.rxn.calculated_reaction_energy_uncertainty == 0
444+
assert np.isnan(self.rxn.calculated_reaction_energy_uncertainty)
445445

446446
def test_calculated_reaction_energy_uncertainty_for_nan(self):
447447
# test that reaction_energy_uncertainty property is nan when the uncertainty

0 commit comments

Comments
 (0)