From 004b3da4ff4d4d00fd2a32bdeebc9e23bcf2bbd0 Mon Sep 17 00:00:00 2001 From: Max Gallant Date: Mon, 29 Jul 2024 18:14:31 -0700 Subject: [PATCH 1/4] Add compute_at_temp to reaction set object --- src/rxn_network/reactions/computed.py | 9 ++++--- src/rxn_network/reactions/open.py | 8 +++--- src/rxn_network/reactions/reaction_set.py | 30 ++++++++++++++++++++--- 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/rxn_network/reactions/computed.py b/src/rxn_network/reactions/computed.py index 6e98fc9b..d87f33e5 100644 --- a/src/rxn_network/reactions/computed.py +++ b/src/rxn_network/reactions/computed.py @@ -5,7 +5,7 @@ from __future__ import annotations from functools import cached_property -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Dict import numpy as np from uncertainties import ufloat @@ -78,14 +78,17 @@ def balance( lowest_num_errors=lowest_num_errors, ) - def get_new_temperature(self, new_temperature: float) -> ComputedReaction: + def get_new_temperature(self, new_temperature: float, entries_at_temp: Dict[Composition, ComputedEntry] = None) -> ComputedReaction: """Returns a new reaction with the temperature changed. Args: new_temperature: New temperature in Kelvin """ try: - new_entries = [e.get_new_temperature(new_temperature) for e in self.entries] + if entries_at_temp is None: + new_entries = [e.get_new_temperature(new_temperature) for e in self.entries] + else: + new_entries = [entries_at_temp.get(e.composition) for e in self.entries] except AttributeError as e: raise AttributeError( "One or more of the entries in the reaction is not associated with a" diff --git a/src/rxn_network/reactions/open.py b/src/rxn_network/reactions/open.py index 93ee1a09..fa1ea499 100644 --- a/src/rxn_network/reactions/open.py +++ b/src/rxn_network/reactions/open.py @@ -104,21 +104,23 @@ def balance( # type: ignore return ComputedReaction(**kwargs) if not chempots else cls(chempots=chempots, **kwargs) - def get_new_temperature(self, new_temperature: float) -> OpenComputedReaction: + def get_new_temperature(self, new_temperature: float, entries_at_temp) -> OpenComputedReaction: """Returns a new reaction with the temperature changed. Args: new_temperature: New temperature in Kelvin """ try: - new_entries = [e.get_new_temperature(new_temperature) for e in self.entries] + if entries_at_temp is None: + new_entries = [e.get_new_temperature(new_temperature) for e in self.entries] + else: + new_entries = [entries_at_temp.get(e.composition) for e in self.entries] except AttributeError as e: raise AttributeError( "One or more of the entries in the reaction is not associated with a" " temperature. Please use the GibbsComputedEntry class for all entries" " in the reaction." ) from e - return OpenComputedReaction( new_entries, self.coefficients, diff --git a/src/rxn_network/reactions/reaction_set.py b/src/rxn_network/reactions/reaction_set.py index 81099ef8..265aef1a 100644 --- a/src/rxn_network/reactions/reaction_set.py +++ b/src/rxn_network/reactions/reaction_set.py @@ -6,7 +6,7 @@ from collections import OrderedDict from functools import lru_cache -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, List, Dict import numpy as np import ray @@ -540,11 +540,35 @@ def set_new_temperature(self, new_temp: float) -> ReactionSet: The new ReactionSet containing the recalculated reactions. """ new_rxns = [] - for rxn in self.get_rxns(): - new_rxns.append(rxn.get_new_temperature(new_temp)) + entries = { e.composition: e.get_new_temperature(new_temp) for e in self.entries } + for rxn in tqdm(list(self.get_rxns())): + new_rxns.append(rxn.get_new_temperature(new_temp, entries)) return ReactionSet.from_rxns(new_rxns, open_elem=self.open_elem, chempot=self.chempot) + def compute_at_temperatures(self, temp_list: List[int]) -> Dict[int, ReactionSet]: + """Recomputes reaction energies at each temperature in a list of temperatures. + + Args: + temp_list : List[int] + The list of temperatures at which new `ReactionSet`s should be generated.`` + + Returns: + Dict[int, ReactionSet] + A mapping of temperature to `ReactionSet` with reaction energies computed at + that temperature. + """ + all_rxns = list(self.get_rxns()) + result = {} + for t in tqdm(temp_list, desc="Computing reaction energies..."): + new_rxns = [] + entries = { e.composition: e.get_new_temperature(t) for e in self.entries } + for rxn in all_rxns: + new_rxns.append(rxn.get_new_temperature(t, entries)) + + result[t] = ReactionSet.from_rxns(new_rxns, open_elem=self.open_elem, chempot=self.chempot) + return result + def _get_rxns_by_indices(self, idxs) -> Iterable[ComputedReaction | OpenComputedReaction]: """Return a list of reactions with the given indices.""" for size, idx_arr in idxs.items(): From d0636f2efcfaca99177a4f190017241cb786fcc5 Mon Sep 17 00:00:00 2001 From: Max Gallant Date: Tue, 24 Feb 2026 11:26:52 -0800 Subject: [PATCH 2/4] some linting --- src/rxn_network/reactions/computed.py | 6 +++++- src/rxn_network/reactions/plotting.py | 12 +----------- src/rxn_network/reactions/reaction_set.py | 10 +++++----- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/rxn_network/reactions/computed.py b/src/rxn_network/reactions/computed.py index d87f33e5..1b532c3c 100644 --- a/src/rxn_network/reactions/computed.py +++ b/src/rxn_network/reactions/computed.py @@ -78,11 +78,15 @@ def balance( lowest_num_errors=lowest_num_errors, ) - def get_new_temperature(self, new_temperature: float, entries_at_temp: Dict[Composition, ComputedEntry] = None) -> ComputedReaction: + def get_new_temperature( + self, new_temperature: float, entries_at_temp: dict[Composition, ComputedEntry] = None + ) -> ComputedReaction: """Returns a new reaction with the temperature changed. Args: new_temperature: New temperature in Kelvin + entries_at_temp: A map of precomputed entries to draw from when recalculating this + entry at a new temperature """ try: if entries_at_temp is None: diff --git a/src/rxn_network/reactions/plotting.py b/src/rxn_network/reactions/plotting.py index 0fc31417..f14c22ea 100644 --- a/src/rxn_network/reactions/plotting.py +++ b/src/rxn_network/reactions/plotting.py @@ -145,17 +145,7 @@ def get_label_and_units(name): if plot_pareto: fig.add_trace(scatter) - hovertemplate = ( - "%{hovertext}
" - "
" - f"{x}" - ": %{x:.3f}" - f" {x_units}" - "
" - f"{y}" - ": %{y:.3f}" - f" {y_units}" - ) + hovertemplate = f"%{{hovertext}}

{x}: %{{x:.3f}} {x_units}
{y}: %{{y:.3f}} {y_units}" if z is not None: hovertemplate = hovertemplate + "
" + f"{z}" + ": %{z:.3f}" + f" {z_units}
" diff --git a/src/rxn_network/reactions/reaction_set.py b/src/rxn_network/reactions/reaction_set.py index 265aef1a..a8ef34e1 100644 --- a/src/rxn_network/reactions/reaction_set.py +++ b/src/rxn_network/reactions/reaction_set.py @@ -6,7 +6,7 @@ from collections import OrderedDict from functools import lru_cache -from typing import TYPE_CHECKING, Any, List, Dict +from typing import TYPE_CHECKING, Any import numpy as np import ray @@ -540,13 +540,13 @@ def set_new_temperature(self, new_temp: float) -> ReactionSet: The new ReactionSet containing the recalculated reactions. """ new_rxns = [] - entries = { e.composition: e.get_new_temperature(new_temp) for e in self.entries } + entries = {e.composition: e.get_new_temperature(new_temp) for e in self.entries} for rxn in tqdm(list(self.get_rxns())): new_rxns.append(rxn.get_new_temperature(new_temp, entries)) return ReactionSet.from_rxns(new_rxns, open_elem=self.open_elem, chempot=self.chempot) - def compute_at_temperatures(self, temp_list: List[int]) -> Dict[int, ReactionSet]: + def compute_at_temperatures(self, temp_list: list[int]) -> dict[int, ReactionSet]: """Recomputes reaction energies at each temperature in a list of temperatures. Args: @@ -562,11 +562,11 @@ def compute_at_temperatures(self, temp_list: List[int]) -> Dict[int, ReactionSet result = {} for t in tqdm(temp_list, desc="Computing reaction energies..."): new_rxns = [] - entries = { e.composition: e.get_new_temperature(t) for e in self.entries } + entries = {e.composition: e.get_new_temperature(t) for e in self.entries} for rxn in all_rxns: new_rxns.append(rxn.get_new_temperature(t, entries)) - result[t] = ReactionSet.from_rxns(new_rxns, open_elem=self.open_elem, chempot=self.chempot) + result[t] = ReactionSet.from_rxns(new_rxns, open_elem=self.open_elem, chempot=self.chempot) return result def _get_rxns_by_indices(self, idxs) -> Iterable[ComputedReaction | OpenComputedReaction]: From 3dbf80e53554a52985ba4980f29bf26faa26fa01 Mon Sep 17 00:00:00 2001 From: Max Gallant Date: Mon, 8 Jun 2026 10:31:19 -0700 Subject: [PATCH 3/4] lint --- src/rxn_network/reactions/computed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rxn_network/reactions/computed.py b/src/rxn_network/reactions/computed.py index 1b532c3c..6c5df92e 100644 --- a/src/rxn_network/reactions/computed.py +++ b/src/rxn_network/reactions/computed.py @@ -5,7 +5,7 @@ from __future__ import annotations from functools import cached_property -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING import numpy as np from uncertainties import ufloat From e72484541fc64c1d8c7540362f5a062c86fd437f Mon Sep 17 00:00:00 2001 From: Max Gallant Date: Mon, 8 Jun 2026 11:12:57 -0700 Subject: [PATCH 4/4] Update docstring --- src/rxn_network/reactions/open.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rxn_network/reactions/open.py b/src/rxn_network/reactions/open.py index fa1ea499..70c8e5d2 100644 --- a/src/rxn_network/reactions/open.py +++ b/src/rxn_network/reactions/open.py @@ -104,11 +104,14 @@ def balance( # type: ignore return ComputedReaction(**kwargs) if not chempots else cls(chempots=chempots, **kwargs) - def get_new_temperature(self, new_temperature: float, entries_at_temp) -> OpenComputedReaction: + def get_new_temperature(self, new_temperature: float, entries_at_temp: dict | None) -> OpenComputedReaction: """Returns a new reaction with the temperature changed. Args: new_temperature: New temperature in Kelvin + entries_at_temp: A dictionary mapping entry compositions to Entry objects with energies + computed at the new temperature. Use this to avoid recomputing entries if you are + applying this method to many reactions. """ try: if entries_at_temp is None: