Complement SMILES¶
Complement SMILES is AutoPoly's format for defining polymer sequences with precise positional control. It extends standard SMILES notation with wildcard atoms ([*]) that mark where each monomer connects to its neighbors.
Why Complement SMILES?¶
In a polymer chain, monomers behave differently depending on their position:
- First monomer — has a starting end group and one connection point
- Middle monomers — have two connection points (to both neighbors)
- Last monomer — has one connection point and an ending end group
Standard SMILES cannot distinguish these positions. Complement SMILES solves this with one rule:
Number of wildcards = number of connections
| Position | Wildcards | Example (ethylene) | Role |
|---|---|---|---|
| First | 1 (right) | "CC[*]" |
Chain start |
| Middle | 2 (both sides) | "[*]CC[*]" |
Interior units |
| Last | 1 (left) | "[*]CC" |
Chain end |
The Three Position Types¶
First position (chain start)¶
Middle positions¶
[*]CC[*]
│ │ │
│ │ └─ Wildcard: connects to the next monomer
│ └──── Carbon backbone
└─────── Wildcard: connects to the previous monomer
Last position (chain end)¶
Building Sequences¶
A chain is an explicit Python list — one complement SMILES per monomer, in order. The degree of polymerization is always len(sequence).
Step 1: Define the middle variant¶
Step 2: Derive first and last¶
Remove the appropriate wildcard:
Step 3: Build the sequence¶
Step 4: Create the polymer¶
from AutoPoly import Polymer
polymer = Polymer(
chain_num=10,
sequence=sequence,
topology="linear",
tacticity="atactic",
)
print(polymer.dop) # 100
A reusable helper:
def uniform_polymer(first: str, middle: str, last: str, dop: int) -> list:
"""Create a uniform homopolymer sequence."""
if dop == 1:
return [middle]
if dop == 2:
return [first, last]
return [first] + [middle] * (dop - 2) + [last]
sequence = uniform_polymer("CC[*]", "[*]CC[*]", "[*]CC", 100)
Common Patterns¶
Uniform homopolymer¶
Block copolymer¶
Contiguous blocks — just mix monomers in the list:
# PE(10)-PS(20)-PE(10) ABA triblock, DOP=40
sequence = (
["CC[*]"] + ["[*]CC[*]"] * 9 + # PE block (10)
["[*]CC([*])c1ccccc1"] * 20 + # PS block (20)
["[*]CC[*]"] * 9 + ["[*]CC"] # PE block (10)
)
Alternating copolymer¶
# Ethylene-styrene alternating, DOP=6
sequence = [
"CC[*]", # A (first)
"[*]CC([*])c1ccccc1", # B
"[*]CC[*]", # A
"[*]CC([*])c1ccccc1", # B
"[*]CC[*]", # A
"[*]CC(c1ccccc1)", # B (last)
]
Comment complex sequences
For long block sequences, annotate block boundaries with inline comments — the sequence is data, and future-you will read it.
Ring vs Linear Polymers¶
Linear polymers¶
Need all three variants (first, middle, last):
sequence_linear = ["CC[*]"] + ["[*]CC[*]"] * 48 + ["[*]CC"]
polymer = Polymer(chain_num=10, sequence=sequence_linear, topology="linear")
Ring polymers¶
Use only the middle variant — every position has two connections:
sequence_ring = ["[*]CC[*]"] * 50 # all middle; no first or last
polymer = Polymer(chain_num=10, sequence=sequence_ring, topology="ring")
Why? Ring polymers have no end groups — every monomer connects to two neighbors. Using a first or last variant in a ring will fail.
Common Monomers Reference¶
Commodity polymers¶
Polyethylene (PE)
Polypropylene (PP)
Polystyrene (PS)
Poly(vinyl chloride) (PVC)
Engineering polymers¶
Poly(methyl methacrylate) (PMMA)
Polyacrylonitrile (PAN)
Poly(ethylene oxide) (PEO)
Specialty polymers¶
Poly(tetrafluoroethylene) (PTFE, Teflon)
Polyisoprene (natural rubber)
Poly(lactic acid) (PLA) — condensation polymer with an ester backbone
Best Practices¶
1. Always check the wildcard count¶
✓ Correct:
❌ Incorrect:
first = "[*]CC[*]" # 2 wildcards — that's a middle monomer
middle = "CC[*]" # 1 wildcard — that's a first monomer
last = "CC" # 0 wildcards — no connection point at all
2. Verify the sequence length¶
sequence = [...]
expected_dop = 50
assert len(sequence) == expected_dop, f"Expected DOP {expected_dop}, got {len(sequence)}"
3. Validate SMILES before a big run¶
from rdkit import Chem
def valid(smiles: str) -> bool:
return Chem.MolFromSmiles(smiles.replace("[*]", "C")) is not None
assert all(valid(s) for s in sequence)
Advanced Topics¶
Custom end groups¶
Change the non-wildcard end of the first/last monomers:
first_OH = "OCC[*]" # hydroxyl start
last_OH = "[*]CCO" # hydroxyl end
first_Br = "BrCC[*]" # bromine start
last_Br = "[*]CCBr" # bromine end
Explicit stereochemistry¶
Use SMILES chirality markers for tacticity control at the monomer level:
isotactic_middle = "[*]C[C@@H]([*])C" # R-configuration
syndiotactic_middle = "[*]C[C@H]([*])C" # S-configuration
For whole-chain tacticity, prefer the tacticity parameter of Polymer ("atactic", "isotactic", "syndiotactic").
Appendix: SMILES Basics¶
Complement SMILES builds on standard SMILES. The essentials:
- Atoms —
Caliphatic carbon,caromatic carbon,O,N, halogensF Cl Br I - Bonds — single (omitted or
-), double=, triple#(e.g.C=C,C#N) - Branches — parentheses:
CC(C)Cis isobutane - Rings — matching digits close a ring:
C1CCCCC1cyclohexane; aromatic rings use lowercase:c1ccccc1benzene - Wildcards —
[*](brackets required; a bare*is invalid)
To find a SMILES for a new monomer, look it up in PubChem or draw it in a chemical editor and export. Then replace the two connection-point hydrogens with [*] wildcards, one per side for a middle monomer.
Next Steps¶
- Polymers vs Molecules — when not to use wildcards
- Force Fields — pick parameters for your chemistry
- Tutorials — sequences in action
- Polymer API — full class reference