Organic molecules have potential applications as both molecular cages and belts. In this project, I develop and explore a method of modeling these structures by using PatternReaction and MoleculeModify to link together cyclohexane into shapes like chains, loops, and belts. I also optimize these shapes by minimizing MMFF energy and compare them to the original structures, and I end by computing the radii of belts to analyze the feasibility of containing molecules in these structures. Finally, I develop an easy-to-use interface for users to construct their own molecules, which could be used for educational purposes in the future. It is worth noting that although these molecules may be modeled on a computer, they may also be incredibly difficult to synthesize in real life.
Introduction
Introduction
Molecular cages have many different applications. For example, they can be used to transport therapeutic drugs for strategic release, capture harmful molecules from water, or act as tiny reaction chambers for unstable reactions that wouldn’t be able to occur otherwise. On the other hand, molecular nanotechnology is a developing field of study, focused on building structures like nanobots or smart materials at atomic levels using the constituent molecules themselves. There’s hope that structures made of cyclohexane could be applicable in both of these fields, creating closed shapes to act as molecular cages or forming gears and belts as a part of a molecular machine. In this project, I explore linkages of cyclohexane like chains, loops, and belts, building them in silico to computationally simulate them.
Creating Chains
Creating Chains
Cyclohexane is a molecule with chemical formula C₆H₁₂ where six carbon atoms form a closed ring, and two hydrogen atoms are attached to each carbon atom. It primarily exists in a stable chair form in nature, constantly flipping between two arrangements. In the process of flipping, cyclohexane takes on intermediate forms known as "boat" and "twist-boat" as well; however, these forms have much more angular and torsional strain than the chair form, so they exist only temporarily and are thus much rarer. Thus, when I refer to and use cyclohexane in this project, it will be in its chair form.
Importing the different forms (conformers) of cyclohexane:
labels={"planar","chair","twist-boat","boat","half-boat","half-chair"};conformers=AssociationThread[labels->CloudImport[CloudObject["https://www.wolframcloud.com/objects/555b1b48-9f89-45ef-a9e2-49c8fe5228b6"],"SDF"]];
Plotting the chair, boat, and twist-boat conformers of cyclohexane:
In[]:=
Grid[{{MoleculePlot3D[conformers[[2]],ImageSize->Small],MoleculePlot3D[conformers[[3]],ImageSize->Small],MoleculePlot3D[conformers[[4]],ImageSize->Small]},{"chair cyclohexane","twist-boat cyclohexane","boat cyclohexane"}}]
Out[]=
chair cyclohexane | twist-boat cyclohexane | boat cyclohexane |
Here, the data for the different conformers of cyclohexane are pulled from a pre-existing Wolfram SDF
file
1
.
2D and 3D renderings of cyclohexane:
In[]:=
Grid[{{MoleculePlot[Molecule["cyclohexane"],ImageSize->Small],MoleculePlot3D[Molecule["cyclohexane"],ImageSize->Small]},{"cyclohexane","cyclohexane (chair form)"}}]
Out[]=
cyclohexane | cyclohexane (chair form) |
Defining a cyclohexane molecule:
In[]:=
cyclohexane=Molecule["cyclohexane"]
Out[]=
Molecule
The simplest shape to make first, and the shape that will form the basis of other future shapes, is a line, or a chain of cyclohexanes. To form this shape recursively, one cyclohexane can be added on at a time to existing chains in order to create chains of arbitrary length n. First, I need to find my base molecule, which will of course be cyclohexane. Then, to figure out how to add additional cyclohexanes on, I’ll consider decalin, which is like two cyclohexanes fused together.
Plotting cyclohexane and decalin:
In[]:=
PlotGrid[{{MoleculePlot[Molecule["cyclohexane"],PlotLabel->"cyclohexane"],MoleculePlot[Molecule["decalin"],PlotLabel->"decalin"]}},ImageSize->Medium]
Out[]=
Defining a decalin molecule:
In[]:=
decalin=Molecule["decalin"]
Out[]=
Molecule
I notice that decalin looks a lot like cyclohexane, only with one of its sides replaced with another cyclohexane! Thus, I might start to think about how I can write a function to automatically detect where an available “side” is, and automatically replace it with a cyclohexane. Fortunately, PatternReaction can do exactly this by taking in two patterns to look for, as well as something to replace those patterns with. More specifically, it should take one side of an existing cyclohexane, and replace it with a ring in order to extend the chain.
Defining the “side” to be replaced:
In[]:=
twoCarbon=MoleculePattern["[C][C]"]
Out[]=
MoleculePattern
Defining the “ring” to replace a side with:
In[]:=
sixCarbon=MoleculePattern["C1CCCCC1"]
Out[]=
MoleculePattern
In PatternReaction, each atom in a molecule is assigned an index. Here, I should explicitly define which atoms on each side of the PatternReaction correspond to which, in order to ensure that each cyclohexane is attached in a consistent and predictable way.
Defining an atom mapping to make sure the new cyclohexane is attached in a consistent manner:
In[]:=
decalinMapping={{1,1}->{1,4},{1,2}->{1,5},{2,6}->{1,6},{2,1}->{1,1},{2,2}->{1,2},{2,3}->{1,3}};
Now, I can write the PatternReaction I’ll be using to make a chain. Note that this won’t actually be a balanced PatternReaction--in real life, two cyclohexanes would never react to produce just one decalin. However, since I’m focused on simply building these molecules on a computer and not the actual reactions, an unbalanced PatternReaction is fine.
A PatternReaction that replaces one “side” of an existing chain with a full cyclohexane:
In[]:=
addCyclohexanePatternReaction=PatternReaction[{twoCarbon,sixCarbon}->{sixCarbon},decalinMapping]
Out[]=
Now that I have a PatternReaction to essentially fuse a cyclohexane onto an existing chain, I can define a function to do it. This is useful, since a function can be nested in order to repeat it any amount of times I want.
Adding one more cyclohexane to a chain of length n:
In[]:=
addCyclohexaneToChain[molecule_]:=ApplyReaction[addCyclohexanePatternReaction,{molecule,cyclohexane}]
Finally, to turn this into a function which can create a chain of an arbitrary length n, I simply use Nest to repeat the function. Nest will take the output from the previous iteration of the function, and use it as the input for the next iteration; essentially, like nesting the function inside of itself. In this fashion, I can add cyclohexanes n-1 times to one existing cyclohexane in order to create a chain of any length n.
Nesting the previous function to create a chain of cyclohexanes:
In[]:=
createChain[n_]:=If[n==1,cyclohexane,Nest[addCyclohexaneToChain,cyclohexane,n-1][[1]]]
Testing the function:
In[]:=
MoleculePlot[createChain[5],ImageSize->Small]
Out[]=
Now, I use MoleculeModify to minimize each atom’s Merck-Molecular Force Field (MMFF) energy. MMFF energy is essentially the sum of an atom’s potential energies, and MoleculeModify will slightly tweak the coordinates of each atom until their energies reach their local minima. This minimization of potential energy tends to rectify unrealistic molecular configurations, turning them into the form they would most likely exist in nature.
Plotting a chain of length 5, as well as an optimized version of it:
In[]:=
Grid[{{MoleculePlot3D[createChain[5],ImageSize->Small],MoleculePlot3D[MoleculeModify[createChain[5],{"EnergyMinimizeAtomCoordinates","MMFF94"}],ImageSize->Small]},{"a 5-chain","an optimized 5-chain"}}]
Out[]=
a 5-chain | an optimized 5-chain |
It seems like these chains are the exact same! Since Mathematica automatically makes a rudimentary attempt to optimize the molecule on its own, this isn’t entirely surprising, but worth mentioning, since optimization usually changes the structure of the molecule by at least a bit.
Creating Wedges
Creating Wedges
Now that I can create a chain, I start to work on wedges. These will be the basis of my hexagons, and are essentially like two cyclohexane chains linked with an angle of 120 degrees between them.
Sadly, my simple approach with PatternReaction only works in one direction--this is because when PatternReaction looks for appropriate sites, it’ll default to the same location every time, so I won’t be able to use that simple PatternReaction to extend in another direction. Thus, if I want to extend into two directions, I’ll need to code in the new atoms and bonds ourselves. In order to do that, I have to know the specific numbers of the atoms we’ll be bonding together, and that depends on the size of the molecule that we’re building onto.
First, though, I’ll build a function that can attach just one cyclohexane to the top of an existing chain. To do that, I’ll have to try and find a pattern in the atom indices.
Sadly, my simple approach with PatternReaction only works in one direction--this is because when PatternReaction looks for appropriate sites, it’ll default to the same location every time, so I won’t be able to use that simple PatternReaction to extend in another direction. Thus, if I want to extend into two directions, I’ll need to code in the new atoms and bonds ourselves. In order to do that, I have to know the specific numbers of the atoms we’ll be bonding together, and that depends on the size of the molecule that we’re building onto.
First, though, I’ll build a function that can attach just one cyclohexane to the top of an existing chain. To do that, I’ll have to try and find a pattern in the atom indices.
Plotting cyclohexane chains of length 4, 5 and 6:
In[]:=
PlotGrid[{{MoleculePlot[createChain[4],AtomLabels->"AtomIndex",PlotLabel->"a cyclo-4 chain"],MoleculePlot[createChain[5],AtomLabels->"AtomIndex",PlotLabel->"a cyclo-5 chain"],MoleculePlot[createChain[6],AtomLabels->"AtomIndex",PlotLabel->"a cyclo-5 chain"]}},PlotRangePadding->Scaled[.07]]
Out[]=
From the plots above a pattern emerges: atom indices increase by 4 each time the chain is lengthened. Thus, the atom indices for the bonds should be a linear function of the length of the chain, which I can figure out with some simple algebra!
Adding one cyclohexane to the top of an existing chain of length m:
In[]:=
createMPlusOne[m_]:=MoleculeModify[MoleculeModify[createChain[m],{"AddAtom",{"C","C","C","C"}}],{"AddBond",{Bond[{4m+3,4m+2}],Bond[{4m+4,4m+3}],Bond[{4m+5,4m+4}],Bond[{4m+6,4m+5}],Bond[{4m+6,4m}]}}]
Now that I have a function that can extend a chain once, how can I make it extend it any amount of times I want? Well, a function to “extend” a wedge with lengths m and n should have atom indices that depend on both m and n, since both contribute to the number of atoms in the molecule. Similarly, it should still be linear in both.
Adding one cyclohexane to the top of an existing wedge with lengths m and n-1:
Note that the atom indices contain both 4m and 4n, as both lengths, no matter what direction, essentially involve four atoms being added each time in either direction. Specifically, the arguments of this function are a wedge with one length m and one length n-1, as well as the numbers m and n. It should also importantly be noted that throughout this essay, my formulae for atom indices assume that hydrogen atoms have not been instantiated. If one does choose to instantiate the hydrogens, they would have to alter the formula by changing coefficients to 10 to account for the extra molecules, among other things.
Now that I have a base molecule and a way to extend, I can combine these functions to add a cyclohexane chain of any length n to a cyclohexane chain of any length m! However, I first have to do a bit of modification on my function to make sure it takes arguments in the right order, and hard code a few cases; this is because the formula I use to calculate atom indices doesn’t work for certain small cases of m and n, and only works when m > n.
Making sure the function takes input in the correct form:
Taking care of the care where the wedge is actually just a chain:
Taking care of the wedge that’s actually just a cyclohexane:
And now, the main body of the function can be written:
Looping the extend upwards function on a chain to create a wedge:
Testing the new function:
Creating Hexagons
Creating Hexagons
Now, the next step is to create hexagons, which I do by combining wedges. Since each wedge contributes two “sides” to the hexagon, I should use three wedges for a total of six sides.
However, in order to combine these molecules, I can’t just make bonds; I have to remove some atoms first, so the others fit cleanly in. For example, in the above image, I have to first remove the two atoms at the very end of each molecule in order to make a clean hexagon.
Creating three wedges, then removing the two atoms at the very end of each:
Note that the coefficients of n here are 0, 8, and 16. This is because when the side length of the hexagon is increased, two cyclohexanes, corresponding to eight carbon molecules, are effectively added onto each wedge, so the indices of the ends of the wedges will increment by 0*8, 1*8 and 2*8 respectively.
Adding bonds between the three wedges:
Something similar applies for when bonds are added except that here the coefficients are 8, 16 and 24, since we’re attaching on the other side of the wedges. Combined, these two functions can finally make hexagons!
First manually creating a hexagon of side length 2, since our formula for indices only works for n >= 3:
First delete atoms, then add bonds to create a hexagon and minimizing its energy:
Creating Belts
Creating Belts
Since I can create chains, a natural extension of this would be to explore the possibility of creating loops, or belts. I approach this in a manner similar to the manner in which I approached hexagons, by deleting atoms and forming new bonds. Fortunately, here, it’s even easier.
A function that creates a chain of length n, then deletes two atoms:
A function that links together the start and end of the chain to form a belt and minimizes energy:
Testing the function:
Finding Interatomic Distance
Finding Interatomic Distance
In order to analyze the feasibility of these structures for use in molecular capture and transport, I decided to look at the size of the largest molecules that could feasibly fit inside these belts; in other words, the “radius” of the belts, at their most narrow point. However, a way to explicitly find the narrowest point of the belt and compute its radius while accounting for the angles of hydrogen bonds proved exceptionally difficult, so below will be a rough approximation instead.
A function that finds the distance between diametrically opposite atoms, divides by two, and subtracts 1 angstrom (approximately the length of one carbon-hydrogen bond, many of which also restrict the space inside the belt).
Now that I have a function to approximate the radius of a belt, I can attempt to find a general function for a belt as a function of the number of cyclohexanes it’s made out of.
Finding the radii of belts with 4 to 35 cyclohexanes:
radiusData saved to a CloudObject for quick access:
Finding a best fit line:
Plotting the exact radii and best fit line:
Note that there are a few points that appear to be outliers, like x=23 and x=24. These occur because large belts, even when optimized for energy, often form twisted ribbon shapes, and the algorithm I use for calculating diametrically opposite points may choose two points that are very close or very far. With this in mind, I will now write a function that, given a molecule’s radius, approximates the size of the belt that would be needed for the molecule to be able to fit through.
A function to calculate the needed size of a belt:
Interface
Interface
Finally, for greater accessibility, I can create an interface that allows the user to choose the function, the inputs, and how it’s displayed!
Creating an interface that allows the user to choose between displaying in 2D or 3D, creating a chain, wedge, hex or belt, and the size of the molecule.
Conclusion
Conclusion
I developed four different functions to build and model cyclohexane chains, wedges, loops, and belts for potential application as molecular cages, gears or belts. In addition, I analyzed the size of these belts, creating a function to reverse engineer the size of the belt needed to accommodate a specific molecule, and created an interface for users to build and investigate these structures themselves!
Future Directions
Future Directions
While it’s currently possible to build these molecules computationally, chemists would face significant challenges when trying to synthesize these molecules in the lab. For example, large connections of cyclohexane in general would face significant strain for molecules being forced into a shape that’s not completely natural, and the closing of large macrocycles (loops) would present large challenges in and of itself. Future directions would likely include studying the feasibility of building these molecules in real life for practical use. On a different front, future directions might include optimizing the speed of the simulation of these molecules, since the interface currently suffers from problems related to speed of updating and computational power.
References
References
Acknowledgements
Acknowledgements
Thanks to my mentor Jason Sonnenberg, Max Wang, and my mentor group of Ananya, Annabella, and Aris for answering my questions and giving me feedback on my project :) Thanks to my TA group for making this entire program so memorable, and my roommate Jonah for letting me sleep early every night. Thanks to Stephen Wolfram for giving me an appropriate direction and scope for my project, and finally, thanks to the greatest TA of all time, Hendry Qianheng Xu.
CITE THIS NOTEBOOK
CITE THIS NOTEBOOK
Building cyclohexane ribbons and macrocycles
by Aaron Fan
Wolfram Community, STAFF PICKS, July 9, 2026
https://community.wolfram.com/groups/-/m/t/3751932
by Aaron Fan
Wolfram Community, STAFF PICKS, July 9, 2026
https://community.wolfram.com/groups/-/m/t/3751932