@@ -3963,19 +3963,21 @@ def _apply_adaptive_reaction_levels(self):
39633963
39643964 Heavy atoms are conserved across a reaction, so the reaction-wide heavy-atom count (the sum over the reactants,
39653965 equal to the TS supermolecule count) keys a single adaptive level for the whole reaction. A reaction
3966- participant whose own heavy-atom count lands it on a *different* adaptive grain than the reaction would
3967- otherwise be evaluated at an inconsistent level, mixing levels of theory across the barrier. To avoid this,
3968- for each such participant whose ``thermo_at_own_level`` is ``True`` (the default), an autonomous relabeled
3969- copy of the species is created from the outset and used by the reaction (evaluated at the reaction-wide
3970- level), while the original species is left to compute its own thermochemistry at its own granular level. If
3971- ``thermo_at_own_level`` is ``False``, the participant itself is evaluated at the reaction-wide level (no copy).
3966+ participant whose own heavy-atom count lands it on a *different* (finer) adaptive grain than the reaction
3967+ would otherwise be evaluated at an inconsistent level, mixing levels of theory across the barrier. To avoid
3968+ this, each such participant whose ``thermo_at_own_level`` is ``False`` (the default) is itself evaluated at
3969+ the reaction-wide level (no copy). If ``thermo_at_own_level`` is ``True``, an autonomous relabeled copy of
3970+ the species is created from the outset and used by the reaction (evaluated at the reaction-wide level),
3971+ while the original species is left to compute its own thermochemistry at its own granular level. A copy is
3972+ also created for a no-copy participant that is shared across reactions landing on different grains, since a
3973+ single per-species override cannot keep both reactions internally consistent.
39723974
39733975 This runs once during setup, before the reactions are processed, so each reaction is defined with its copies
39743976 from the start (its label, reactants, and products are kept mutually consistent for ``check_attributes`` and
39753977 restart).
39763978 """
3977- for i , rxn in enumerate (self .rxn_list ):
3978- rxn_index = rxn .index if rxn .index is not None else i
3979+ for rxn_i , rxn in enumerate (self .rxn_list ):
3980+ rxn_index = rxn .index if rxn .index is not None else rxn_i
39793981 reactant_species = [self .species_dict [label ] for label in rxn .reactants if label in self .species_dict ]
39803982 if len (reactant_species ) != len (rxn .reactants ) \
39813983 or any (spc .number_of_heavy_atoms is None for spc in reactant_species ):
@@ -3985,24 +3987,30 @@ def _apply_adaptive_reaction_levels(self):
39853987 reaction_n_heavy = sum (spc .number_of_heavy_atoms for spc in reactant_species )
39863988 reaction_range = self ._adaptive_atom_range (reaction_n_heavy )
39873989 for participants in (rxn .reactants , rxn .products ):
3988- for i , label in enumerate (participants ):
3990+ for pos , label in enumerate (participants ):
39893991 spc = self .species_dict .get (label )
39903992 if spc is None or spc .number_of_heavy_atoms is None \
39913993 or self ._adaptive_atom_range (spc .number_of_heavy_atoms ) == reaction_range :
39923994 continue
39933995 if not spc .thermo_at_own_level :
3994- spc .adaptive_lot_n_heavy = reaction_n_heavy
3995- continue
3996+ if spc .adaptive_lot_n_heavy is None \
3997+ or self ._adaptive_atom_range (spc .adaptive_lot_n_heavy ) == reaction_range :
3998+ spc .adaptive_lot_n_heavy = reaction_n_heavy
3999+ continue
4000+ logger .warning (f'Species { label } participates in reactions on different adaptive level '
4001+ f'grains, creating a dedicated copy of it for reaction { rxn .label } to keep '
4002+ f'the reaction internally consistent.' )
39964003 copy_label = check_label (f'{ label } _TS{ rxn_index } ' )[0 ]
3997- copy_spc = spc .copy ()
3998- copy_spc .label = copy_label
3999- copy_spc .adaptive_lot_n_heavy = reaction_n_heavy
4000- copy_spc .compute_thermo = False
4001- copy_spc .include_in_thermo_lib = False
4002- self .species_list .append (copy_spc )
4003- self .species_dict [copy_label ] = copy_spc
4004- self .initialize_output_dict (copy_label )
4005- participants [i ] = copy_label
4004+ if copy_label not in self .species_dict :
4005+ copy_spc = spc .copy ()
4006+ copy_spc .label = copy_label
4007+ copy_spc .adaptive_lot_n_heavy = reaction_n_heavy
4008+ copy_spc .compute_thermo = False
4009+ copy_spc .include_in_thermo_lib = False
4010+ self .species_list .append (copy_spc )
4011+ self .species_dict [copy_label ] = copy_spc
4012+ self .initialize_output_dict (copy_label )
4013+ participants [pos ] = copy_label
40064014 rxn .label = rxn .arrow .join ([rxn .plus .join (rxn .reactants ), rxn .plus .join (rxn .products )])
40074015
40084016 def initialize_output_dict (self , label : str | None = None ):
0 commit comments