File: fix-build-with-new-uncertainties.patch

package info (click to toggle)
pymatgen 2025.2.18%2Bdfsg1-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 85,888 kB
  • sloc: python: 176,173; javascript: 780; makefile: 221; sh: 78
file content (190 lines) | stat: -rw-r--r-- 8,034 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
From: =?UTF-8?q?Se=C3=A1n=20Kavanagh?= <51478689+kavanase@users.noreply.github.com>
Subject: Use `np.nan` instead of 0 for no uncertainty with `ufloat`, to avoid unnecessary warnings (#4400)
Origin: https://github.com/materialsproject/pymatgen/commit/22834931904cfd138206a57684fff20ba0469580

* 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 <shyuep@users.noreply.github.com>
---
 src/pymatgen/analysis/reaction_calculator.py | 13 +++++++---
 src/pymatgen/entries/compatibility.py        | 26 ++++++++++++--------
 src/pymatgen/entries/computed_entries.py     | 10 ++++----
 tests/analysis/test_reaction_calculator.py   |  2 +-
 4 files changed, 31 insertions(+), 20 deletions(-)

--- a/src/pymatgen/analysis/reaction_calculator.py
+++ b/src/pymatgen/analysis/reaction_calculator.py
@@ -10,7 +10,7 @@
 import numpy as np
 from monty.fractions import gcd_float
 from monty.json import MontyDecoder, MSONable
-from uncertainties import ufloat
+from uncertainties import UFloat, ufloat
 
 from pymatgen.core.composition import Composition
 from pymatgen.entries.computed_entries import ComputedEntry
@@ -100,7 +100,7 @@
     __repr__ = __str__
 
     @overload
-    def calculate_energy(self, energies: dict[Composition, ufloat]) -> ufloat:
+    def calculate_energy(self, energies: dict[Composition, ufloat]) -> UFloat:
         pass
 
     @overload
@@ -485,10 +485,15 @@
 
         for entry in self._reactant_entries + self._product_entries:
             comp, factor = entry.composition.get_reduced_composition_and_factor()
-            energy_ufloat = ufloat(entry.energy, entry.correction_uncertainty)
+            energy_ufloat = (
+                ufloat(entry.energy, entry.correction_uncertainty)
+                if entry.correction_uncertainty and not np.isnan(entry.correction_uncertainty)
+                else entry.energy
+            )
             calc_energies[comp] = min(calc_energies.get(comp, float("inf")), energy_ufloat / factor)
 
-        return self.calculate_energy(calc_energies).std_dev
+        ufloat_reaction_energy = self.calculate_energy(calc_energies)
+        return ufloat_reaction_energy.std_dev if isinstance(ufloat_reaction_energy, UFloat) else np.nan
 
     def as_dict(self) -> dict:
         """
--- a/src/pymatgen/entries/compatibility.py
+++ b/src/pymatgen/entries/compatibility.py
@@ -121,16 +121,14 @@
         """
         new_corr = self.get_correction(entry)
         old_std_dev = entry.correction_uncertainty
-        if np.isnan(old_std_dev):
-            old_std_dev = 0
-        old_corr = ufloat(entry.correction, old_std_dev)
+        old_corr = ufloat(entry.correction, 0 if np.isnan(old_std_dev) else old_std_dev)
         updated_corr = new_corr + old_corr
 
         # if there are no error values available for the corrections applied,
         # set correction uncertainty to not a number
-        uncertainty = np.nan if updated_corr.nominal_value != 0 and updated_corr.std_dev == 0 else updated_corr.std_dev
-
-        entry.energy_adjustments.append(ConstantEnergyAdjustment(updated_corr.nominal_value, uncertainty))
+        entry.energy_adjustments.append(
+            ConstantEnergyAdjustment(updated_corr.nominal_value, updated_corr.std_dev or np.nan)
+        )
 
         return entry
 
@@ -195,7 +193,7 @@
             ufloat: 0.0 +/- 0.0 (from uncertainties package)
         """
         if SETTINGS.get("PMG_POTCAR_CHECKS") is False or not self.check_potcar:
-            return ufloat(0.0, 0.0)
+            return ufloat(0.0, np.nan)
 
         potcar_spec = entry.parameters.get("potcar_spec")
         if self.check_hash:
@@ -211,7 +209,7 @@
         expected_psp = {self.valid_potcars.get(el.symbol) for el in entry.elements}
         if expected_psp != psp_settings:
             raise CompatibilityError(f"Incompatible POTCAR {psp_settings}, expected {expected_psp}")
-        return ufloat(0.0, 0.0)
+        return ufloat(0.0, np.nan)
 
 
 @cached_class
@@ -249,6 +247,8 @@
         if rform in self.cpd_energies:
             correction += self.cpd_energies[rform] * comp.num_atoms - entry.uncorrected_energy
 
+        if correction.std_dev == 0:
+            correction = ufloat(correction.nominal_value, np.nan)  # set std_dev to nan if no uncertainty
         return correction
 
 
@@ -286,7 +286,7 @@
         """
         comp = entry.composition
         if len(comp) == 1:  # Skip element entry
-            return ufloat(0.0, 0.0)
+            return ufloat(0.0, np.nan)
 
         correction = ufloat(0.0, 0.0)
 
@@ -345,6 +345,8 @@
             else:
                 correction += self.oxide_correction["oxide"] * comp["O"]
 
+        if correction.std_dev == 0:
+            correction = ufloat(correction.nominal_value, np.nan)  # set std_dev to nan if no uncertainty
         return correction
 
 
@@ -432,6 +434,8 @@
                 correction += ufloat(-1 * MU_H2O * nH2O, 0.0)
                 # correction += 0.5 * 2.46 * nH2O  # this is the old way this correction was calculated
 
+        if correction.std_dev == 0:
+            correction = ufloat(correction.nominal_value, np.nan)  # set std_dev to nan if no uncertainty
         return correction
 
 
@@ -537,6 +541,8 @@
             if sym in u_corr:
                 correction += ufloat(u_corr[sym], u_errors[sym]) * comp[el]
 
+        if correction.std_dev == 0:
+            correction = ufloat(correction.nominal_value, np.nan)  # set std_dev to nan if no uncertainty
         return correction
 
 
@@ -1077,7 +1083,7 @@
             )
 
         # check the POTCAR symbols
-        # this should return ufloat(0, 0) or raise a CompatibilityError or ValueError
+        # this should return ufloat(0, np.nan) or raise a CompatibilityError or ValueError
         if entry.parameters.get("software", "vasp") == "vasp":
             pc = PotcarCorrection(
                 MPRelaxSet,
--- a/src/pymatgen/entries/computed_entries.py
+++ b/src/pymatgen/entries/computed_entries.py
@@ -363,8 +363,8 @@
         Returns:
             float: the total energy correction / adjustment applied to the entry in eV.
         """
-        # adds to ufloat(0.0, 0.0) to ensure that no corrections still result in ufloat object
-        corr = ufloat(0.0, 0.0) + sum(ufloat(ea.value, ea.uncertainty) for ea in self.energy_adjustments)
+        # either sum of adjustments or ufloat with nan std_dev, so that no corrections still result in ufloat object:
+        corr = sum(ufloat(ea.value, ea.uncertainty) for ea in self.energy_adjustments) or ufloat(0.0, np.nan)
         return corr.nominal_value
 
     @correction.setter
@@ -386,11 +386,11 @@
         Returns:
             float: the uncertainty of the energy adjustments applied to the entry in eV.
         """
-        # adds to ufloat(0.0, 0.0) to ensure that no corrections still result in ufloat object
-        unc = ufloat(0.0, 0.0) + sum(
+        # either sum of adjustments or ufloat with nan std_dev, so that no corrections still result in ufloat object:
+        unc = sum(
             (ufloat(ea.value, ea.uncertainty) if not np.isnan(ea.uncertainty) else ufloat(ea.value, 0))
             for ea in self.energy_adjustments
-        )
+        ) or ufloat(0.0, np.nan)
 
         if unc.nominal_value != 0 and unc.std_dev == 0:
             return np.nan
--- a/tests/analysis/test_reaction_calculator.py
+++ b/tests/analysis/test_reaction_calculator.py
@@ -444,7 +444,7 @@
     def test_calculated_reaction_energy_uncertainty_for_no_uncertainty(self):
         # test that reaction_energy_uncertainty property doesn't cause errors
         # when products/reactants have no uncertainties
-        assert self.rxn.calculated_reaction_energy_uncertainty == 0
+        assert np.isnan(self.rxn.calculated_reaction_energy_uncertainty)
 
     def test_calculated_reaction_energy_uncertainty_for_nan(self):
         # test that reaction_energy_uncertainty property is nan when the uncertainty