diff --git a/src/rxn_network/reactions/computed.py b/src/rxn_network/reactions/computed.py index 6e98fc9b..6c5df92e 100644 --- a/src/rxn_network/reactions/computed.py +++ b/src/rxn_network/reactions/computed.py @@ -78,14 +78,21 @@ 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 + entries_at_temp: A map of precomputed entries to draw from when recalculating this + entry at a new temperature """ 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..70c8e5d2 100644 --- a/src/rxn_network/reactions/open.py +++ b/src/rxn_network/reactions/open.py @@ -104,21 +104,26 @@ 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: 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: - 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/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 81099ef8..a8ef34e1 100644 --- a/src/rxn_network/reactions/reaction_set.py +++ b/src/rxn_network/reactions/reaction_set.py @@ -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():