<jupyter_start><jupyter_text>SMID calculationThe smallest maximum intramolecular distance (SMID) is as a new and useful molecular size descriptor.  is meant to reflect how small a conformation a molecule can adopt within a low-energy window and can be conceptualized as an effective diameter. It is shown that for several proprietary and public PXR binding and activation data sets, SMID is correlated with the likelihood of PXR activation and CYP3A4 upregulation.The histogram is showing the fraction of molecules within each SMID range that display low, medium, or high levels of PXR activation relative to the rifampicin control. The PXR activation assay displayed a distinct preference for molecules with SMID values between 9 and 14 Å, with a steadily decreasing percentage of molecules strongly activating PXR above 11 Å.![Image](histogram.jpeg)Ref: [J. Chem. Inf. Model. 2020, 60, 4, 2091–2099](https://doi.org/10.1021/acs.jcim.9b00692)based on [smid.py / github](https://github.com/BrianGoldman/SMID/blob/master/smid.py)---<jupyter_code>import io
import math
import tempfile
from pathlib import Path
from itertools import combinations
from rdkit.Chem import AllChem
from rdkit.Chem import Draw
from rdkit.Chem.Draw import IPythonConsole, rdMolDraw2D, SimilarityMaps
from ipywidgets import interact_manual, FileUpload, Output
from IPython.display import display, display_png, FileLink, Markdown
from tqdm.notebook import tqdm
def atmDist(atmPair):
    '''
    Calculates the *squared* distance in space between 2 coordinates
    '''
    (c1, c2) = atmPair
    return math.pow(c1[0] - c2[0], 2) + math.pow(c1[1] - c2[1], 2) + math.pow(c1[2] - c2[2], 2)

def max_intram_dist(mol):
    '''
    Returns the maximum *squared* distance within a molecule
    '''
    comb = combinations(mol.GetPositions(),2)
    return max([atmDist(pair) for pair in comb])

def calculate_smid(mol):
    try:
        # Prepare molecule
        mol = AllChem.AddHs(mol)           
          
        # Generate conformations and minimizes the geometry
        if AllChem.CalcNumRotatableBonds(mol) > 10:
            num_conf = 1000
        else:
            num_conf = 200
        
        # cids -> conformer id's
        cids = AllChem.EmbedMultipleConfs(mol, num_conf, numThreads=0, pruneRmsThresh=1.0, \
                                          useExpTorsionAnglePrefs=True, useBasicKnowledge=True, \
                                          enforceChirality=False)
        
        # optimize/minimize conformers, result is a 2 tuple containing convergence (0 or 1) and energy
        result = AllChem.MMFFOptimizeMoleculeConfs(mol,numThreads=0, mmffVariant='MMFF94s',)
        
        # filter based on convergence and energy window (5 kcal)
        min_energy = min([energy for (conv, energy) in result if conv])
        energy_filter = [id for id, (conv, energy) in enumerate(result) if conv and (energy < (min_energy + 5))]

        # calculate max_intram_dist for each conformer
        mid_list = []
        for cid in energy_filter:
            mid_list.append(max_intram_dist(mol.GetConformer(cid)))

        # return SMID
        return math.sqrt(min(mid_list))
   
    except:
        return None<jupyter_output><empty_output><jupyter_text>Process SD file- Click the Upload button to select an SD file- Click *Run Interact* to start the calculation.A new SD file will be generated with the calculated SMID for each structure in the field *CIM_SMID*<jupyter_code>@interact_manual(upload = FileUpload(accept='.sdf', multiple=False))
def process_sdf(upload):
    out = Path(tempfile.NamedTemporaryFile(prefix='SMID-', suffix='.sdf',delete=False, dir='.').name)
    
    content = list(upload.values())[0]['content']
    suppl = AllChem.ForwardSDMolSupplier(io.BytesIO(content))
    writer = AllChem.SDWriter(out.name)

    molecules = [mol for mol in suppl]

    for mol in tqdm(molecules):    
        smid = calculate_smid(mol)
        mol.SetProp('CIM_SMID',str(round(smid,2)))
        writer.write(mol)
        writer.flush()    

    writer.close()
    display(Markdown('''## Done
    Download the results with the link below
                     '''))
    display(FileLink(out.name))<jupyter_output><empty_output><jupyter_text>H-ScanThis script will systematically replaces each nonequivalent hydrogen atom with a phenyl group, recalculate the SMID, and records the change in SMID at each position. It will return a map of SMID changes projected onto the original structure.- Paste a SMILES string in the text box- Click *Run Interact* to start the H-Scan.<jupyter_code>@interact_manual(smiles='')
def hScan(smiles):
    m = AllChem.MolFromSmiles(smiles)
    AllChem.Compute2DCoords(m)
    seenSet = set()

    ref_smid = calculate_smid(m)

    m = AllChem.AddHs(m)

    hAtms = [atom for atom in m.GetAtoms() if atom.GetSymbol() == 'H']

    for a in tqdm(hAtms):
        benz = AllChem.MolFromSmiles("c1c[c:1]ccc1")
        merged = AllChem.CombineMols(m, benz)
        edit = AllChem.EditableMol(merged)

        connected_to_H = a.GetNeighbors()  
        atoms_to_bond = [_.GetIdx() for _ in merged.GetAtoms() if _.GetAtomMapNum() == 1 or _.GetIdx() == connected_to_H[0].GetIdx()]

        edit.AddBond(atoms_to_bond[0], atoms_to_bond[1], order=AllChem.rdchem.BondType.SINGLE)
        edit.RemoveBond(connected_to_H[0].GetIdx(), a.GetIdx())
        edit.RemoveAtom(a.GetIdx())

        smi = AllChem.MolToSmiles(edit.GetMol())

        if smi not in seenSet:
            seenSet.add(smi)
            smid_diff = calculate_smid(AllChem.MolFromSmiles(smi)) - ref_smid    
            connected_to_H[0].SetProp('atomNote', f'{smid_diff:.1f}')

    print(f'SMID: {ref_smid:.1f}')
    
    m = AllChem.RemoveHs(m)

    contribs = [float(m.GetAtomWithIdx(i).GetProp('atomNote')) if m.GetAtomWithIdx(i).HasProp('atomNote') else 0 for i in range(m.GetNumAtoms())]
    d = Draw.MolDraw2DCairo(400, 400)
    d.SetFontSize(6)
    SimilarityMaps.GetSimilarityMapFromWeights(m, contribs, contourLines=6, draw2d=d)
    d.FinishDrawing()
    display_png(d.GetDrawingText(), raw=True)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09988537743572949, 'fraction_numerical': 0.016210905518257736, 'mean_word_length': 3.365975696926376, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '484104', 'license': 'MIT', 'n_tokens_mistral': 2079, 'n_tokens_neox': 1916, 'n_words': 535, 'path': 'SMID-webapp.ipynb', 'repo_name': 'wsmts/rdkit-smid', 'size': '6107'}