<jupyter_start><jupyter_text>Examples of Use for rings.py Module This module is the crown jewel of ZSE. I have developed algorithms using graph theory that are able to find rings in zeolite frameworks. There are a number of uses for this information. I use ring finding to in the zse.cation.py module to place cations in each of the rings associated with a T-site. Our group uses the rings associated with an oxygen or a T-site to classify them in a more descriptive way than just saying O1 or T4. Most recently I have been using the rings associated with an oxygen site, tetrahedral site, and entire framework as descriptors for neural networks. I have been able to predict deprotonoation energies, and proton location energies using these methods.Various "rules" have been published to determine which cycles to include in ring finding. I have implemented 3 of those rules in ZSE.All rings found follow the rule presented by Goetzke and <NAME> (https://doi.org/10.1016/0022-3093(91)90145-V). The algorithm used to find those rings is also an implementation of the algorithm presented in that same paper. This is the default validation method applied by ZSE. Sastre and Corma presented a rule that counts all of the rings that pass through an atom in a zeolite framework (https://doi.org/10.1021/jp8100128). This rule counts a ring if for any oxygen-oxygen first nearest neighbor pair along the cycle, the cycle is the shortest path that connects them. Use "validation = 'sastre'" to get results in accordance with this rule.Vertex symbols are the conventional way of describing T-sites in zeolites, shown (https://doi.org/10.1016/S0144-2449(97)00133-4). The IZA Structure Database (http://www.iza-structure.org/databases/) lists these results as well. A T-site has 6 oxygen-oxygen pairs associated with it, the vertex symbol lists the shortest ring connecting each oxygen-oxygen pair, as well as any degeracies. Use "validation = 'vertex'" to get results in accordance with this rule. **Note:** This rule only works when the index provided is a T-site, ZSE cannot find vertex symbols for an oxygen atom in the zeolite framework. Lastly, I have created my own rule that finds a subset of rings between the Goetzke and Sastre methods. Stacked even membered rings result in a composite ring that traverses both the top and bottom stacked rings. An example is a 14-MR in AFI because of the stacked 12-MRs. This ring fits the definition according to Goetzke and Klein, however may not be relevant for researchers. My new rule removes these composite rings. Use "validation='crum'" to get results in accordance with this rule.  rings.get_rings( )This is the basic building block of all the ring finding in ZSE. We can use this function to find all the rings associated with a T-site or oxygen atom in the zeolite framework.**Note** This works best if you remove any adsorbates from your framework first.  Inputs**z** is an atoms object containing your zeolite framework \**index** is the index of the T-site or oxygen atom you want to find the rings for \**validation** optional, method for determining valid rings  - If None (default) returns all rings following the rule reported by <NAME>.; <NAME>. (DOI: 10.1016/0022-3093(91)90145-V)  - 'sastre' returns rings found by the Sastre, G. and Corma, A. method (DOI: 10.1021/jp8100128)  - 'vertex' finds all the rings contained in the vertex symbol of a T-site, defined by O'Keeffe, M. and Hyde, S.T. (DOI: 10.1016/S0144-2449(97)00133-4)  - 'crum' implements my rule that removes stacked composite rings  **max_ring** Max ring size to look for ( of T-sites), default=12 Outputs**ring_list** is a list of all the ring sizes associated with **index** \**paths** contains the indices of the atoms that make up each ring \**ring_atoms** is a list atoms objects that only contains atoms in the rings, this is convenient for viewing with ASE's visualizer\**atoms** is a larger unit cell of the zeolite framework, big enough to contain the largest ring found<jupyter_code>from zse.collections import *
from zse.rings import *
from zse.utilities import *
from ase.visualize import view
import numpy as np<jupyter_output><empty_output><jupyter_text>Let's check results from each validation method on a T-site in CHA:<jupyter_code>z = framework('CHA')
print('Validation\tRings Found')
for v in [None,'crum','sastre','vertex']:
    ring_list, paths, ring_atoms, atoms = get_rings(z,101,validation=v,max_ring=12)
    print('{0}\t\t{1}'.format(v,ring_list))<jupyter_output>Validation	Rings Found
None		[4, 4, 4, 6, 8, 8, 8, 8, 8, 8, 12]
crum		[4, 4, 4, 6, 8, 8, 12]
sastre		[4, 4, 4, 6, 8, 8]
vertex		[4, 4, 4, 6, 8, 8]<jupyter_text>Above I presented the rules from least restrictive to most restrictive.Next, if you want the atom indices of the atoms that make up the rings, you can do the following:<jupyter_code>print('Ring Size \t Path')
for r,p in zip(ring_list,paths):
    print('{0} \t\t {1}'.format(r,p))
# We can also get the T-site and O-site labels for the rings if the framework is an IZA framework:
# Right now you have to get this information manually, 
# but in the future there will be a function to do this for you.
inds = np.arange(len(atoms)).reshape(-1,len(z))
labels = site_labels(z,'CHA')
print('Ring Size \t Path')
for r,p in zip(ring_list,paths):
    path = [labels[int(np.where(inds==x)[1])] for x in p]
    print('{0} \t\t {1}'.format(r,path))<jupyter_output>Ring Size 	 Path
4 		 ['T1', 'O1', 'T1', 'O2', 'T1', 'O1', 'T1', 'O2']
4 		 ['T1', 'O1', 'T1', 'O4', 'T1', 'O3', 'T1', 'O4']
4 		 ['T1', 'O3', 'T1', 'O4', 'T1', 'O1', 'T1', 'O4']
6 		 ['T1', 'O3', 'T1', 'O1', 'T1', 'O3', 'T1', 'O1', 'T1', 'O3', 'T1', 'O1']
8 		 ['T1', 'O2', 'T1', 'O3', 'T1', 'O2', 'T1', 'O4', 'T1', 'O2', 'T1', 'O3', 'T1', 'O2', 'T1', 'O4']
8 		 ['T1', 'O3', 'T1', 'O2', 'T1', 'O4', 'T1', 'O2', 'T1', 'O3', 'T1', 'O2', 'T1', 'O4', 'T1', 'O2']<jupyter_text>NotesA couple things to note here is that when people discuss rings in zeolites, a 6-membered ring (6-MR) contains 6 tetrahedral sites. So a 6-MR actually contains 12 atoms total. \This particular oxygen (index = 0) is associated with three rings: a 6-MR and two 4-MRs. \We can also visualize these rings below.<jupyter_code># Let's visualize some of the rings:
# You can view all the rings with:
view(ring_atoms)

# Or pick a specific ring to view using its index:
view(ring_atoms[4])<jupyter_output><empty_output><jupyter_text>rings.get_unique_rings( )We can also find all the unique rings of a framework (based on geometry). To do this we will find all the rings associated with each type of unique T-site in the framwork. Add all the rings together and use some tricks to remove duplicates. The inputs and outputs of this function are slightly different from what we had above.  Inputs**z** is an atoms object containing your zeolite framework \**tsite** list of the unique T-site indices for the framework, you can get these with the utilities.get_tsites() command for IZA frameworks \**validation** optional, method for determining valid rings (see above) \**max_ring** Max ring size to look for ( of T-sites), default=12 Outputs**ring_list** is a list of all the ring sizes associated with **index** \**paths** contains the indices of the atoms that make up each ring \**ring_atoms** is a list ASE atoms objects that only contains atoms in the rings, this is convenient for viewing with ASE's visualizer\**atoms** is a larger unit cell of the zeolite framework, big enough to contain the largest ring found<jupyter_code># I'm going to use a different framework from CHA here
# Since CHA only has one unique T-site, it doesn't have many unique rings

z = framework('TON')

# ZSE has a built in method to get the unique T-site indices
tsites, tmults, tinds = get_tsites('TON')

print('T-Site\tMultiplicity\tExample Index')
for s,m,i in zip(tsites,tmults,tinds):
    print('{0}\t{1}\t\t{2}'.format(s,m,i))

# Use the T-Site example indices to find all unique rings
ring_list, paths, ring_atoms, atoms = get_unique_rings(z, tinds,validation=None,max_ring=10)
print('List of unique rings:')
print(ring_list)

print('\nAtom indices making each ring:')
for r in paths:
    print(r)

print('\nAtom labels for each ring:')
inds = np.arange(len(atoms)).reshape(-1,len(z))
labels = site_labels(z,'TON')
for r,p in zip(ring_list,paths):
    path = [labels[int(np.where(inds==x)[1])] for x in p]
    print(path)<jupyter_output>List of unique rings:
[5, 6, 6, 6, 10]

Atom indices making each ring:
[48, 8, 68, 43, 427, 391, 423, 32, 56, 16]
[48, 0, 844, 804, 862, 479, 497, 473, 502, 444, 484, 4]
[48, 0, 844, 804, 862, 806, 846, 2, 50, 10, 68, 8]
[48, 4, 484, 452, 492, 468, 491, 467, 852, 812, 844, 0]
[48, 0, 844, 812, 852, 820, 858, 2550, 2582, 2542, 2574, 1730, 1778, 1746, 1786, 1754, 64, 24, 56, 16]

Atom labels for each ring:
['T1', 'O2', 'T4', 'O6', 'T3', 'O4', 'T2', 'O5', 'T2', 'O3']
['T1', 'O1', 'T1', 'O2', 'T4', 'O6', 'T3', 'O6', 'T4', 'O2', 'T1', 'O1']
['T1', 'O1', 'T1', 'O2', 'T4', 'O2', 'T1', 'O1', 'T1', 'O2', 'T4', 'O2']
['T1', 'O1', 'T1', 'O3', 'T2', 'O5', 'T2', 'O5', 'T2', 'O3', 'T1', 'O1']
['T1', 'O1', 'T1', 'O3', 'T2', 'O4', 'T3', 'O4', 'T2', 'O3', 'T1', 'O1', 'T1', 'O3', 'T2', 'O4', 'T3', 'O4', 'T2', 'O3']
{'fraction_non_alphanumeric': 0.1272372274650179, 'fraction_numerical': 0.05466970387243736, 'mean_word_length': 4.587878787878788, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 3, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '238578', 'license': 'MIT', 'n_tokens_mistral': 3410, 'n_tokens_neox': 3055, 'n_words': 1373, 'path': 'examples/04_rings_examples.ipynb', 'repo_name': 'cwaitt/zse', 'size': '9231'}