Reaction Modeling¶
MolPy’s reacter layer lets you describe chemistry in a programmable way:
- Define how two reactive sites (ports) find their anchor atoms
- Define which atoms leave (e.g. hydrogens)
- Define how new bonds are created
Instead of hard‑coding reactions per system, you build reusable Reacter
objects and plug them into builders (e.g. polymer assembly).
1. Reacter: programmable reaction recipe¶
At the core is the Reacter class (molpy.reacter.base.Reacter), which
represents one reaction type:
anchor_left/anchor_right– select reactive atoms from each monomerleaving_left/leaving_right– select atoms to removebond_maker– create the new bond(s)
A minimal example sketch (using pre‑defined selectors):
from molpy.reacter import Reacter
from molpy.reacter.selectors import port_anchor_selector, remove_one_H
from molpy.reacter.transformers import make_single_bond
cc_coupling = Reacter(
name="C-C_coupling_with_H_loss",
anchor_left=port_anchor_selector,
anchor_right=port_anchor_selector,
leaving_left=remove_one_H,
leaving_right=remove_one_H,
bond_maker=make_single_bond,
)
The actual monomer objects are Atomistic‑based wrappers (e.g. Monomer)
that expose ports marking possible reaction sites.
2. Running a single reaction¶
Reacter.run takes two monomer‑like objects plus explicit port names and
returns a ProductSet:
from molpy.core.wrappers.monomer import Monomer
# Assume mono_A and mono_B are Monomer wrappers around Atomistic structures
product_set = cc_coupling.run(
mono_A,
mono_B,
port_L="1", # explicit ports
port_R="2",
)
product = product_set.product # Atomistic product
notes = product_set.notes # metadata about what happened
print("New atoms:", len(list(product.atoms)))
print("New bonds:", len(list(product.bonds)))
print("Reaction name:", notes.get("reaction_name", cc_coupling.name))
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[2], line 5 1 from molpy.core.wrappers.monomer import Monomer 3 # Assume mono_A and mono_B are Monomer wrappers around Atomistic structures 4 product_set = cc_coupling.run( ----> 5 mono_A, 6 mono_B, 7 port_L="1", # explicit ports 8 port_R="2", 9 ) 11 product = product_set.product # Atomistic product 12 notes = product_set.notes # metadata about what happened NameError: name 'mono_A' is not defined
Important:
- Port selection is explicit – no hidden heuristics; you always decide which ports to connect.
- The reaction is run on copies of input monomers, so originals are not mutated.
3. Using reactions in polymer builders¶
Most users won’t call Reacter.run directly. Instead, you plug Reacter
instances into higher‑level builders, such as the linear polymer builder in
molpy.builder.polymer.linear.
from molpy.builder.polymer.connectors import ReacterConnector
from molpy.builder.polymer.linear import linear
from molpy.reacter import Reacter
from molpy.reacter.selectors import port_anchor_selector, remove_one_H
from molpy.reacter.transformers import make_single_bond
# 1. Define a base reaction
cc_coupling = Reacter(
name="C-C_coupling",
anchor_left=port_anchor_selector,
anchor_right=port_anchor_selector,
leaving_left=remove_one_H,
leaving_right=remove_one_H,
bond_maker=make_single_bond,
)
# 2. Connector manages which Reacter to use for which monomer pair
connector = ReacterConnector(
default=cc_coupling,
port_map={
("A", "B"): ("port_A1", "port_B1"),
("B", "A"): ("port_B2", "port_A2"),
},
)
# 3. Monomer library & sequence
mono_A = Monomer(inner=A_atomistic, name="A")
mono_B = Monomer(inner=B_atomistic, name="B")
library = {"A": mono_A, "B": mono_B}
polymer = linear(
sequence="ABABAB",
library=library,
connector=connector,
)
print(polymer) # Polymer wrapper around an Atomistic
print("atoms:", len(list(polymer.inner.atoms)))
print("bonds:", len(list(polymer.inner.bonds)))
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In[3], line 27 18 connector = ReacterConnector( 19 default=cc_coupling, 20 port_map={ (...) 23 }, 24 ) 26 # 3. Monomer library & sequence ---> 27 mono_A = Monomer(inner=A_atomistic, name="A") 28 mono_B = Monomer(inner=B_atomistic, name="B") 29 library = {"A": mono_A, "B": mono_B} NameError: name 'A_atomistic' is not defined
Here the data model looks like:
- Monomer/Polymer wrappers wrap
Atomisticstructures Reacterdefines one chemical stepReacterConnectorchooses whichReacterto use for each monomer pair- Builders orchestrate many reaction steps to produce polymers
4. Where to go next¶
- For the underlying data structures (
Atomistic, ports, wrappers), see:user-guide/data-structures.mduser-guide/molecular-building.md
- For a full notebook‑style reaction workflow, see:
tutorials/reaction-modeling.ipynb
The goal of the reacter layer is to keep reaction logic explicit and reusable, while letting higher‑level builders compose many reactions into larger systems.