Make the RMG family set configurable via settings, resolved in get_all_families - #978
Open
calvinp0 wants to merge 1 commit into
Open
Make the RMG family set configurable via settings, resolved in get_all_families#978calvinp0 wants to merge 1 commit into
calvinp0 wants to merge 1 commit into
Conversation
calvinp0
force-pushed
the
feature_configurable_rmg_family_set
branch
2 times, most recently
from
August 13, 2026 16:13
a68e891 to
24becfe
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #978 +/- ##
==========================================
+ Coverage 64.15% 64.20% +0.04%
==========================================
Files 119 119
Lines 39539 39557 +18
Branches 10261 10266 +5
==========================================
+ Hits 25366 25396 +30
+ Misses 11205 11189 -16
- Partials 2968 2972 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
calvinp0
force-pushed
the
feature_configurable_rmg_family_set
branch
from
August 13, 2026 21:06
24becfe to
e4f05d8
Compare
…l_families
ARC hard-coded the 'default' RMG family set. It is the signature default of
get_all_families() and of every entry point above it, so there was no way to ask ARC
to consider anything else short of naming a set at each call site. That set is RMG's
recommended list, which is right for mechanism generation but excludes families ARC's
TS adapters declare support for, so a run targeting specific reactions could not reach
them at all.
A new top-level setting, rmg_family_set, now supplies that value, and it is resolved
inside get_all_families() -- the single sink every path funnels through, and which
already owned an in-body fallback for its own default. get_reaction_family_products(),
check_family_name(), and ARCReaction.get_product_dicts()/determine_family() propagate
None to mean "not specified".
Resolving it in the sink rather than in each signature is the point. Threading the
setting through the signatures instead leaves get_all_families()'s own default at the
literal 'default', so every call site that does not pass the setting on keeps returning
the curated set. Measured on a deployed installation whose ~/.arc/settings.py asked for
'all': settings['rmg_family_set'] read back as 'all', an explicit
get_all_families('all') returned 100 families, and a bare get_all_families() returned
55. On this branch's parent, check_family_name() is exactly such a bare call site.
determine_family()'s shortcut to the cached product_dicts property now keys off
rmg_family_set being None rather than equal to 'default', so an explicitly requested
set is always honoured even when it matches the configured one.
Setting rmg_family_set to 'all' is only useful if 'all' can reach the families in
question, and it could not: it unioned the family sets named in RMG's recommended.py,
and families such as Intra_RH_Add_Exocyclic and Intra_RH_Add_Endocyclic appear in none
of them despite shipping in the database. get_all_families() now also unions the
families that exist as RMG database directories, listed by the new
get_rmg_family_directories(), which counts a directory as a family only when it holds a
groups.py template. The union is de-duplicated, which also removes the 24 duplicate
labels the recommended sets alone produced.
The directories are appended after the recommended sets, so a family reachable only
through them is always positioned last and wins only where no recommended family
matched. Every candidate still has to reproduce the products the reaction asserts, so
such a family cannot introduce a transformation, only propose a mechanism for one
already stated. That ordering is a contract rather than an accident, so it is stated in
the docstring and pinned by a test: sorting the result or de-duplicating it through a
set would silently break it.
This redefines what 'all' means, and 'all' is not only reachable through the setting:
the Linear TS adapter asks for it unconditionally whenever the configured set yields no
product dicts. An installation that never touches the setting is therefore affected,
which a test over that path records -- 1,4-cyclohexadiene <=> benzene + H2 has no family
under the shipped default and resolves to H2_Loss under that retry.
get_reaction_family_products()'s docstring claimed 'all' excludes surface families
outright, and two further statements repeated it. 'all' skips family sets whose label
contains 'surface' and surface family directories, but a set with a non-surface label
can still list one, as electrochem does for the Surface_Proton_Electron_Reduction_*
families. The three statements now say what the code does, and a test pins it.
Tests cover both directions: that the shipped default leaves get_all_families()
unchanged, and that the configured set governs get_all_families(), check_family_name(),
get_product_dicts() and determine_family() when no set is named at the call site.
2-methyl-1-butene <=> 1,1-dimethylcyclopropane is covered end to end as a family
reachable only through the database directories, and OH + HO2 <=> H2O2 + O as a reaction
that matches both a recommended family and a directory-only one, where the recommended
family wins.
calvinp0
force-pushed
the
feature_configurable_rmg_family_set
branch
from
August 15, 2026 15:07
e4f05d8 to
361976b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Base:
main. First of three — followed by #979 (deterministic ordering + authoritative pinning) and #982 (chemical gates on the family choice). Read in that order.The bug
settings['rmg_family_set']had no effect on a bareget_all_families()call, because the function's own signature default was the literal'default'.get_all_families()is the sink every family query funnels through, so threading the setting through callers leaves the sink untouched and every call site that does not forward the value keeps returning the curated set.check_family_name()onmainis exactly such a bare call site (family.py:1285, verified against44a6b112).Measured on a deployed installation whose
~/.arc/settings.pyasked for'all':get_all_families('all')get_all_families('default')get_all_families()This is a missed call site, not a stale value:
arc/imports.pymerges the~/.arcoverlay at module level, so a module-level constant captures the overlaid value correctly. Only the bare call was broken.The fix
One line in the sink, which already owned an in-body fallback for its own default:
Every caller now propagates
Noneto mean "not specified", so no call site can miss it.determine_family()'s shortcut to the cachedproduct_dictsproperty keys offrmg_family_set is Nonerather than== 'default', so an explicitly requested set is honoured even when it happens to match the configured one.New setting
rmg_family_set = 'default'inarc/settings/settings.py, documented in place.Making
'all'actually reach the families'all'previously unioned only the sets named in RMG'srecommended.py. Some families ship in the database without appearing in any set —Intra_RH_Add_ExocyclicandIntra_RH_Add_Endocyclicamong them.get_all_families()now also unions the families that exist as RMG database directories, via a newget_rmg_family_directories(). A directory counts only when it holds agroups.pytemplate.defaultandch_pyrolysis, so no distinct family is lost.Acceptance case, 2-methyl-1-butene ⇌ 1,1-dimethylcyclopropane (
C=C(C)CC <=> CC1(C)CC1):rmg_family_set'default'NoneNone'all'NoneIntra_RH_Add_ExocyclicCounts come from two different RMG-database checkouts — treat them separately. The 100/55 table above is from the deployed installation. Measured locally on this branch's head against
RMG-databaseat/home/calvin/code/RMG-database:'default'→ 54 families;'all'→ 99 families.'all'but not'default'; 11 are in'all'but in no recommended set at all (the pure directory scan):H2_Loss,Intra_RH_Add_Endocyclic,Intra_RH_Add_Exocyclic,Intra_R_Add_ExoTetCyclic,Li_Addition_MultipleBond,Li_NO_Ring_Opening,Li_NO_Substitution,R_Addition_CSm,SubstitutionS,Substitution_O,lone_electron_pair_bond.This redefines
'all', and'all'reaches a stock install'all'is not only reachable through the setting.linear.py:1734asks for it unconditionally whenever the configured set yields no product dicts, so an installation that never touches the setting is affected. That block is pre-existing onmainand is not modified here.Measured on the
linear.pypath — 1,4-cyclohexadiene ⇌ benzene + H₂, at the shipped'default':product_dictsorigin/mainNoneH2_Losstest_wider_family_set_scan_used_by_the_linear_ts_adapterpins this._family/_product_dicts/_atom_mapin afinally, but only after the whole weights loop, whileTSGuess(family=rxn.family, ...)is written during it — so the reaction object is restored and the persisted artifacts keep the wider family. Not changed here; noted because this PR alters what that path sees.Newly-reachable families have strictly lowest priority
get_rmg_family_directories()returns sorted names andget_all_families()appends them after the recommended union, so a directory-only family sits at positions 85–95 of the 96 RMG families.get_reaction_family_productsiterates in order and ARC takesproduct_dicts[0].family=None.get_all_families()docstring and pinned bytest_directory_only_families_are_ordered_lastand, end to end, bytest_widening_keeps_the_recommended_family_when_both_matchon[OH] + [O]O ⇌ OO + [O], whereH_Abstraction(recommended) andSubstitution_O(directory-only) each match in isolation.PYTHONHASHSEEDvalues). Make reaction family determination deterministic and honour a pinned family #979 replaces the concatenation with a sort within each tier, which keeps the guarantee and additionally makes the order hash-seed-independent. Reviewers should read the two together rather than treating this PR's concatenation as final.Why no allowlist on the directory union
Each of the 11 was probed by its firing conditions rather than by its exclusion:
lone_electron_pair_bondneeds a closed-shell singlet O (*2 = O u0 p3 c0) and returns 0 matches with triplet O;H2_Lossis one fixed 8-atom group;R_Addition_CSmneeds CS itself and co-matchesR_Addition_MultipleBond, which wins on ordering; the threeLi_*needLi u1 p0 c0.RMG's curation inverts against TS-search utility, so "RMG left it out, so it is unreliable" condemns exactly the families the benchmark needs:
An allowlist would be a second hand-maintained curation list drifting from
ts_adapters_by_rmg_family, which ARC already maintains — and a hand-picked list would have excludedlone_electron_pair_bond, a family ARC declares support for and ships a passing test for (linear_test.py::test_interpolate_lone_electron_pair_bond). Seen that way this PR does not expose a new hazard: it makes ARC's own already-tested support reachable.Surface families
'all'containsSurface_Proton_Electron_Reduction_{Alpha, Alpha_vdW, Beta, Beta_vdW, Beta_Dissociation}on both trees — they arrive via theelectrochemset, whose label contains no'surface', so theif 'surface' not in family_set_labelfilter never sees them. A test pins this actual behaviour.Note for callers
get_product_dicts()orget_all_families()is now sensitive to process-global settings state. That is the fix, but it is a footgun for tests and scripts: without settingrmg_family_setexplicitly you are silently exercising whatever is configured, which ships as'default'.rmg_family_set=explicitly.Scope and parity
get_all_families()at the shipped'default'is byte-identical tomain, andtest_rmg_family_set_setting_ships_as_defaultasserts the shipped constant.'all', per thelinear.pysection above.main— same 5 pre-existingtorch_ani_test.pyfailures on both, no new ones, 9 tests added.origin/main:test_wider_family_set_scan_used_by_the_linear_ts_adapter,test_widening_keeps_the_recommended_family_when_both_match,test_bare_calls_honour_the_rmg_family_set_setting,test_determine_family_reaches_a_directory_only_family.Reuse check
settings[key]" resolver), by name acrossdef resolve*/get_setting*, and for the rawor settings[...]idiom at call sites. Nothing of the kind exists; the onlyresolve*helper touching settings isimports.py::resolve_overridden_dependents, which re-derives dependent paths. The fallback therefore stays inline inget_all_families(), where one already was.kinetics/families(get_all_familiesscansARC_FAMILIES_PATHfor ARC's own families only), soget_rmg_family_directories()is new and sits besideget_rmg_recommended_family_sets().Supersedes
fix_family_set(916d9943), which carries the same feature without the sink fix.