<jupyter_start><jupyter_text>data_generation.ipynb Purpose of this notebookThis notebook shows an example of a workflow to generate a set of molecules to study. Beginning with some principal molecules (in this case, Lithium Ethylene Carbonate (LiEC) and water (H2O), we first generate a small set of fragments and recombine these fragments to form new molecules. A similar principal-fragment-recombinant workflow was used to generate LIBE. What you getA collection of fragment molecule graphs (`all_frags`) and recombinant molecules (`combos`). This notebook will also generate the input files necessary to use BonDNet to study the thermodynamics of the possible recombination reactions (for more details on BonDNet, see the [package documentation](https://github.com/mjwen/bondnet)). What you DON'T getIn LIBE, the recombinant molecules were limited by several filters. Only fragments that could be formed by exergonic pathways were allowed to recombine, and the recombinant molecules generated were limited by prediction of their stability (using BonDNet). Such filters are not employed here.An additional limitation is that we do not here show the user how to perform DFT calculations on the fragment or recombinant molecules. This was not included because some users of LIBE may not have access to Q-Chem, the DFT code used to generate this dataset.<jupyter_code>from pathlib import Path
import copy

from pymatgen.core.structure import Molecule
from pymatgen.analysis.graphs import MoleculeGraph
from pymatgen.analysis.local_env import OpenBabelNN, metal_edge_extender
from pymatgen.analysis.fragmenter import Fragmenter

import deliberate.recombination as recomb
molecules_dir = Path().resolve().parent / "molecules"
liec = Molecule.from_file((molecules_dir / "LiEC.xyz").as_posix())
h2o = Molecule.from_file((molecules_dir / "H2O.xyz").as_posix())<jupyter_output><empty_output><jupyter_text>In a single-step fragmentation process (`depth=1`), all bonds are broken in the initial molecule (here, water), and the resulting molecule sub-graphs are gathered to generate a dictionary of fragments. The resulting dictionary (`water_frags`) has alphabetical formulas as keys (in this example, `H1 O1` and `H1` will be keys), and lists of MoleculeGraphs as values.<jupyter_code>water_frags = Fragmenter(h2o, depth=1)
print("Number of fragments from water:", water_frags.total_unique_fragments)<jupyter_output><empty_output><jupyter_text>Because ethylene carbonate has a ring structure, we have to declare `open_rings=True`. This will use a cheap force-field method to generate an initial structure of the molecule with each ring-bond broken. We also include the fragments from `water_frags` so that duplicate fragments are not generated.<jupyter_code>all_frags = Fragmenter(liec, depth=1, open_rings=True, prev_unique_frag_dict=water_frags.unique_frag_dict)
print("Total number of fragments (H2O + LiEC):", all_frags.total_unique_fragments)
charges = [-1, 0, 1]
all_molecule_graphs = list()

# Add all fragments
for _, fragment_list in all_frags.unique_frag_dict.items():
    for fragment in fragment_list:
        for charge in charges:
            mg = copy.deepcopy(fragment)
            mg.molecule.set_charge_and_spin(charge)
            all_molecule_graphs.append(mg)
        
# Also add principal molecules
for charge in charges:
    h2o_mg = MoleculeGraph.with_local_env_strategy(h2o, OpenBabelNN())
    h2o_mg.molecule.set_charge_and_spin(charge)
    all_molecule_graphs.append(h2o_mg)
    
    liec_mg = MoleculeGraph.with_local_env_strategy(liec, OpenBabelNN())
    liec_mg = metal_edge_extender(liec_mg)
    liec_mg.molecule.set_charge_and_spin(charge)
    all_molecule_graphs.append(liec_mg)
print("Total number of molecule graphs:", len(all_molecule_graphs))<jupyter_output><empty_output><jupyter_text>After generating fragments, we then use those fragments (and the principal molecules) to generate new recombinant molecules. Details on this process can be found in `src/deliberate/recombination.py` in this repository. In brief, the process is:1. Each molecule graph in the initial set is examined to see what sites, if any, are available for bonding. This is based on valence rules - for instance, a carbon atom will be considered available if it has less than 4 bonds. Hydrogen and lithium are only allowed to recombine if they are not bonded to anything (they are isolated atoms)2. Each molecule is allowed to recombine with each other molecule (including itself) via all possible combinations of available sites.As a byproduct of this process, two files will be generated: `combos.txt` contains indices relevant to recombination "reactions", and `mol_graphs_recombination.json` contains all recombinant molecule graphs. NOTE: generating combinations is a rather slow process. The next cell may take several minutes to run! It should also be noted that generating recombinant molecules is inherently combinatorially and scales accordingly.<jupyter_code>combos = recomb.generate_combinations(all_molecule_graphs, Path(".").resolve())
print("Number of recombinant molecules generated", len(combos))<jupyter_output><empty_output><jupyter_text>In an actual workflow, we would use BonDNet to predict the bond dissociation energies for each bond formed via a recombination reaction. This is a way to predict which recombinant molecules should be expected to be stable. While we do not here demonstrate the use of BonDNet, the following cell will generate all files necessary to use BonDNet on this test dataset.<jupyter_code>recomb.generate_bondnet_files(all_molecule_graphs, combos, recomb.parse_combinations_file(Path(".").resolve() / "combinations.txt"), Path("."))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.06427320490367776, 'fraction_numerical': 0.0036777583187390544, 'mean_word_length': 5.750591016548463, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 21, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '567846', 'license': 'MIT', 'n_tokens_mistral': 1655, 'n_tokens_neox': 1498, 'n_words': 637, 'path': 'notebooks/data_generation.ipynb', 'repo_name': 'espottesmith/deliberate', 'size': '5710'}