{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":87793,"databundleVersionId":11403143,"sourceType":"competition"},{"sourceId":7639698,"sourceType":"datasetVersion","datasetId":4299272},{"sourceId":8318191,"sourceType":"datasetVersion","datasetId":4459124},{"sourceId":11031560,"sourceType":"datasetVersion","datasetId":6870588}],"dockerImageVersionId":30918,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false},"papermill":{"default_parameters":{},"duration":5842.209454,"end_time":"2025-02-27T02:24:24.986198","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2025-02-27T00:47:02.776744","version":"2.6.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# https://www.kaggle.com/code/shujun717/ribonanzanet-3d-finetune\n# https://www.deepseek.com/","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:08.022516Z","iopub.execute_input":"2025-03-14T17:54:08.023052Z","iopub.status.idle":"2025-03-14T17:54:08.028893Z","shell.execute_reply.started":"2025-03-14T17:54:08.023003Z","shell.execute_reply":"2025-03-14T17:54:08.027349Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"This code provides a pipeline to convert a sequence of cartesian coordinates xyz to the corresponding internal **B**ond**A**ngle**T**orsion coordinates and back to original cartesian space with negligible error. The main motivation is to achieve a set of coordinates constant to translations and rotations. Here some reconstructed vs original examples:","metadata":{}},{"cell_type":"code","source":"from IPython.display import Image\nImage(filename='/kaggle/input/examples/example_291.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:08.030527Z","iopub.execute_input":"2025-03-14T17:54:08.030994Z","iopub.status.idle":"2025-03-14T17:54:08.077500Z","shell.execute_reply.started":"2025-03-14T17:54:08.030949Z","shell.execute_reply":"2025-03-14T17:54:08.075927Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"Image(filename='/kaggle/input/examples/example_639.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:08.080015Z","iopub.execute_input":"2025-03-14T17:54:08.080512Z","iopub.status.idle":"2025-03-14T17:54:08.096123Z","shell.execute_reply.started":"2025-03-14T17:54:08.080462Z","shell.execute_reply":"2025-03-14T17:54:08.094669Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"Image(filename='/kaggle/input/examples/example_705.png')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:08.098326Z","iopub.execute_input":"2025-03-14T17:54:08.098799Z","iopub.status.idle":"2025-03-14T17:54:08.118580Z","shell.execute_reply.started":"2025-03-14T17:54:08.098762Z","shell.execute_reply":"2025-03-14T17:54:08.116820Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport random\nimport pickle\nfrom tqdm import tqdm\nimport plotly.graph_objects as go\n\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset\nfrom fastai.vision.all import *\n\ndevice = 'cuda' if torch.cuda.is_available() else 'cpu'","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","execution":{"iopub.status.busy":"2025-03-14T17:54:08.120020Z","iopub.execute_input":"2025-03-14T17:54:08.120460Z","iopub.status.idle":"2025-03-14T17:54:22.833928Z","shell.execute_reply.started":"2025-03-14T17:54:08.120420Z","shell.execute_reply":"2025-03-14T17:54:22.832838Z"},"papermill":{"duration":3.877674,"end_time":"2025-02-27T00:47:09.256216","exception":false,"start_time":"2025-02-27T00:47:05.378542","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#set seed for everything\ntorch.manual_seed(0)\nnp.random.seed(0)\nrandom.seed(0)","metadata":{"execution":{"iopub.status.busy":"2025-03-14T17:54:22.834947Z","iopub.execute_input":"2025-03-14T17:54:22.835285Z","iopub.status.idle":"2025-03-14T17:54:22.848731Z","shell.execute_reply.started":"2025-03-14T17:54:22.835232Z","shell.execute_reply":"2025-03-14T17:54:22.847402Z"},"papermill":{"duration":0.013119,"end_time":"2025-02-27T00:47:09.274201","exception":false,"start_time":"2025-02-27T00:47:09.261082","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Config","metadata":{"papermill":{"duration":0.004021,"end_time":"2025-02-27T00:47:09.282613","exception":false,"start_time":"2025-02-27T00:47:09.278592","status":"completed"},"tags":[]}},{"cell_type":"code","source":"config = {\n    \"seed\": 0,\n    \"cutoff_date\": \"2020-01-01\",\n    \"test_cutoff_date\": \"2022-05-01\",\n    \"max_len\": 384,\n    \"batch_size\": 1,\n    \"learning_rate\": 1e-4,\n    \"weight_decay\": 0.0,\n    \"mixed_precision\": \"bf16\",\n    \"model_config_path\": \"../working/configs/pairwise.yaml\",  # Adjust path as needed\n    \"epochs\": 10,\n    \"cos_epoch\": 5,\n    \"loss_power_scale\": 1.0,\n    \"max_cycles\": 1,\n    \"grad_clip\": 0.1,\n    \"gradient_accumulation_steps\": 1,\n    \"d_clamp\": 30,\n    \"max_len_filter\": 9999999,\n    \"min_len_filter\": 10, \n    \"structural_violation_epoch\": 50,\n    \"balance_weight\": False,\n}","metadata":{"execution":{"iopub.status.busy":"2025-03-14T17:54:22.849757Z","iopub.execute_input":"2025-03-14T17:54:22.850015Z","iopub.status.idle":"2025-03-14T17:54:22.873124Z","shell.execute_reply.started":"2025-03-14T17:54:22.849993Z","shell.execute_reply":"2025-03-14T17:54:22.871763Z"},"papermill":{"duration":0.009597,"end_time":"2025-02-27T00:47:09.296419","exception":false,"start_time":"2025-02-27T00:47:09.286822","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Get data and do some data processing¶\n","metadata":{"execution":{"iopub.execute_input":"2025-02-27T00:35:07.639840Z","iopub.status.busy":"2025-02-27T00:35:07.639563Z","iopub.status.idle":"2025-02-27T00:35:07.643454Z","shell.execute_reply":"2025-02-27T00:35:07.642590Z","shell.execute_reply.started":"2025-02-27T00:35:07.639817Z"},"papermill":{"duration":0.004546,"end_time":"2025-02-27T00:47:09.305252","exception":false,"start_time":"2025-02-27T00:47:09.300706","status":"completed"},"tags":[]}},{"cell_type":"code","source":"train_sequences=pd.read_csv(\"/kaggle/input/stanford-rna-3d-folding/train_sequences.csv\")\ntrain_labels = pd.read_csv(\"/kaggle/input/stanford-rna-3d-folding/train_labels.csv\")","metadata":{"execution":{"iopub.status.busy":"2025-03-14T17:54:22.876694Z","iopub.execute_input":"2025-03-14T17:54:22.877006Z","iopub.status.idle":"2025-03-14T17:54:23.281173Z","shell.execute_reply.started":"2025-03-14T17:54:22.876984Z","shell.execute_reply":"2025-03-14T17:54:23.280055Z"},"papermill":{"duration":0.364507,"end_time":"2025-02-27T00:47:09.674057","exception":false,"start_time":"2025-02-27T00:47:09.309550","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_labels[\"pdb_id\"] = train_labels[\"ID\"].apply(lambda x: x.split(\"_\")[0]+'_'+x.split(\"_\")[1])\ntrain_labels[\"pdb_id\"]","metadata":{"execution":{"iopub.status.busy":"2025-03-14T17:54:23.282788Z","iopub.execute_input":"2025-03-14T17:54:23.283109Z","iopub.status.idle":"2025-03-14T17:54:23.399457Z","shell.execute_reply.started":"2025-03-14T17:54:23.283081Z","shell.execute_reply":"2025-03-14T17:54:23.398195Z"},"papermill":{"duration":0.093374,"end_time":"2025-02-27T00:47:09.772231","exception":false,"start_time":"2025-02-27T00:47:09.678857","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Ignore warnings\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:23.400979Z","iopub.execute_input":"2025-03-14T17:54:23.401492Z","iopub.status.idle":"2025-03-14T17:54:23.407457Z","shell.execute_reply.started":"2025-03-14T17:54:23.401455Z","shell.execute_reply":"2025-03-14T17:54:23.405994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"all_xyz = []\nfor pdb_id in tqdm(train_sequences['target_id']):\n    df = train_labels[train_labels[\"pdb_id\"]==pdb_id]\n    xyz = df[['x_1','y_1','z_1']].to_numpy().astype('float32')\n    xyz[xyz < -1e17] = np.nan\n    all_xyz.append(xyz)\nprint('Done!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:23.409027Z","iopub.execute_input":"2025-03-14T17:54:23.409547Z","iopub.status.idle":"2025-03-14T17:54:34.263409Z","shell.execute_reply.started":"2025-03-14T17:54:23.409485Z","shell.execute_reply":"2025-03-14T17:54:34.261848Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def angle_between_vectors(u, v):\n#   Compute the dot product\n    dot_product = np.dot(u, v)    \n#   Compute the magnitudes of the vectors\n    magnitude_u = np.linalg.norm(u)\n    magnitude_v = np.linalg.norm(v)    \n#   Compute the cosine of the angle\n    cos_theta = dot_product / (magnitude_u * magnitude_v)    \n#   Compute the angle in radians\n    theta = np.arccos(cos_theta)\n    \n    return theta","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:34.264462Z","iopub.execute_input":"2025-03-14T17:54:34.264789Z","iopub.status.idle":"2025-03-14T17:54:34.270957Z","shell.execute_reply.started":"2025-03-14T17:54:34.264762Z","shell.execute_reply":"2025-03-14T17:54:34.269463Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def givens_rotation(M, k, i, j):\n    \"\"\"\n    Compute the ij Givens rotation matrix to zero out M[k, i].\n    \"\"\"\n    a = M[k, i]\n    b = M[k, j]\n    r = np.hypot(a, b)\n    \n    G = np.eye(3)\n    if r == 0: return G\n    \n#   Compute c and s based on the order of i and j\n    if i > j:\n        c = b / r\n        s = a / r\n    else:\n        c = a / r\n        s = -b / r\n    \n#   Construct the Givens rotation matrix\n    G[i, i] = c\n    G[j, j] = c\n    G[i, j] = s\n    G[j, i] = -s\n    \n    return G\n\n# Example matrix\nM = np.random.rand(3,3)*5\n\nprint(\"Original matrix M:\")\nprint(M)\n\n# Zero out the top triangle\nM = M @ givens_rotation(M, 0, 0, 1)\nM = M @ givens_rotation(M, 0, 0, 2)\nM = M @ givens_rotation(M, 1, 1, 2)\n    \nprint(\"\\nMatrix after zeroing top triangle:\")\nprint(M)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:34.271805Z","iopub.execute_input":"2025-03-14T17:54:34.272127Z","iopub.status.idle":"2025-03-14T17:54:34.317707Z","shell.execute_reply.started":"2025-03-14T17:54:34.272099Z","shell.execute_reply":"2025-03-14T17:54:34.316008Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"filter_nan = []\nmax_len = 0\nfor xyz in all_xyz:\n    if len(xyz) > max_len:\n        max_len = len(xyz)\n\n    filter_nan.append((np.isnan(xyz).mean() <= 0.5) & \\\n                      (len(xyz)<config['max_len_filter']) & \\\n                      (len(xyz)>config['min_len_filter']))\n\nprint(f\"Longest sequence in train: {max_len}\")\n\nfilter_nan = np.array(filter_nan)\nnon_nan_indices = np.arange(len(filter_nan))[filter_nan]\n\ntrain_sequences = train_sequences.loc[non_nan_indices].reset_index(drop=True)\nall_xyz = [all_xyz[i] for i in non_nan_indices]","metadata":{"execution":{"iopub.status.busy":"2025-03-14T17:54:34.318996Z","iopub.execute_input":"2025-03-14T17:54:34.319501Z","iopub.status.idle":"2025-03-14T17:54:34.356047Z","shell.execute_reply.started":"2025-03-14T17:54:34.319458Z","shell.execute_reply":"2025-03-14T17:54:34.354690Z"},"papermill":{"duration":0.031297,"end_time":"2025-02-27T00:47:18.139959","exception":false,"start_time":"2025-02-27T00:47:18.108662","status":"completed"},"tags":[],"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plt_xyz(xyz,gt,A='prediction',B='gt',size=5,color=None,colorscale='Viridis',opacity=.8):\n    fig = go.Figure(data=[\n        go.Scatter3d(\n            x=xyz[:,0], y=xyz[:,1], z=xyz[:,2],\n            mode='markers',\n            name = A,\n            marker=dict(\n                size=size,\n                color=color,\n                colorscale=colorscale,\n                opacity=opacity\n            )\n        ),\n        go.Scatter3d(\n            x=gt[:,0], y=gt[:,1], z=gt[:,2],\n            mode='lines',\n            name=B,\n            marker=dict(\n                size=size,\n                colorscale=colorscale,\n                opacity=opacity\n            )\n        )])\n\n    fig.show() ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:34.357055Z","iopub.execute_input":"2025-03-14T17:54:34.357456Z","iopub.status.idle":"2025-03-14T17:54:34.365587Z","shell.execute_reply.started":"2025-03-14T17:54:34.357409Z","shell.execute_reply":"2025-03-14T17:54:34.364251Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def check_reconstruction(reconstructed,original,display=False):\n    mask = (reconstructed.isnan().any(1) + original.isnan().any(1)) == 0\n    reconstructed = reconstructed[mask]\n    original = original[mask]\n    reconstructed -= reconstructed.mean(0)\n    original -= original.mean(0)\n\n    cov_matrix = reconstructed.T @ original\n    U, S, Vt = torch.svd(cov_matrix)\n    R = Vt @ U.T\n    if torch.det(R) < 0:\n        Vt[-1, :] *= -1\n        R = Vt @ U.T\n    reconstructed = reconstructed @ R.T\n    \n    if display: plt_xyz(reconstructed,original,A='reconstucted',B='original',color=reconstructed[:,2])\n    rmsd = ((original - reconstructed)**2).sum(1).mean().sqrt()\n    return rmsd","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:34.366773Z","iopub.execute_input":"2025-03-14T17:54:34.367101Z","iopub.status.idle":"2025-03-14T17:54:34.388300Z","shell.execute_reply.started":"2025-03-14T17:54:34.367073Z","shell.execute_reply":"2025-03-14T17:54:34.386357Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calculate_internal_coordinates(coords):\n    n = coords.shape[0]\n    bonds = []\n    angles = []\n    torsions = []\n    \n    # Bond lengths\n    for i in range(1, n):\n        bonds.append(np.linalg.norm(coords[i] - coords[i-1]))\n    \n    # Bond angles\n    for i in range(2, n):\n        v1 = coords[i-2] - coords[i-1]\n        v2 = coords[i] - coords[i-1]\n        cos_theta = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))\n        angles.append(np.degrees(np.arccos(np.clip(cos_theta, -1.0, 1.0))))\n    \n    # Torsion angles (sign corrected)\n    for i in range(3, n):\n        p1, p2, p3, p4 = coords[i-3], coords[i-2], coords[i-1], coords[i]\n        v1 = p2 - p1\n        v2 = p3 - p2\n        v3 = p4 - p3\n        \n        n1 = np.cross(v2, v1)\n        n2 = np.cross(v3, v2)\n        \n        n1 /= np.linalg.norm(n1) + 1e-8  # Avoid division by zero\n        n2 /= np.linalg.norm(n2) + 1e-8\n        v2 /= np.linalg.norm(v2) + 1e-8\n        \n        m1 = np.cross(n1, v2)\n        x = np.dot(n1, n2)\n        y = np.dot(m1, n2)\n        \n        torsion = np.degrees(np.arctan2(y, x))\n        torsions.append(-torsion)  # Invert the sign here\n    \n    return bonds, angles, torsions","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:34.389848Z","iopub.execute_input":"2025-03-14T17:54:34.390296Z","iopub.status.idle":"2025-03-14T17:54:34.415410Z","shell.execute_reply.started":"2025-03-14T17:54:34.390215Z","shell.execute_reply":"2025-03-14T17:54:34.414139Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def reconstruct_from_internal(bonds, angles, torsions):\n    n = len(bonds) + 1\n    coords = np.zeros((n, 3))\n    \n    # Fix first three atoms in reference frame\n    if n >= 1: coords[0] = [0, 0, 0]\n    if n >= 2: coords[1] = [bonds[0], 0, 0]\n    if n >= 3:\n        theta = np.radians(180 - angles[0])\n        x = bonds[1] * np.cos(theta) + coords[1][0]\n        y = bonds[1] * np.sin(theta)\n        coords[2] = [x, y, 0]\n    \n    # Build subsequent atoms (corrected m_vec calculation)\n    for i in range(3, n):\n        a, b, c = coords[i-3], coords[i-2], coords[i-1]\n        bond = bonds[i-1]\n        angle = np.radians(angles[i-2])\n        torsion = np.radians(torsions[i-3])\n        \n        # Local coordinate system\n        bc = c - b\n        bc_norm = bc / np.linalg.norm(bc)\n        n_vec = np.cross(b - a, bc)\n        n_norm = n_vec / np.linalg.norm(n_vec)\n        m_vec = np.cross(n_norm, bc_norm)  # Corrected cross product order\n        \n        # Displacement components\n        dx = -bond * np.cos(angle)\n        dy = bond * np.sin(angle) * np.cos(torsion)\n        dz = bond * np.sin(angle) * np.sin(torsion)\n        \n        # Global displacement\n        displacement = dx * bc_norm + dy * m_vec + dz * n_norm\n        coords[i] = c + displacement\n    \n    return coords","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:34.416700Z","iopub.execute_input":"2025-03-14T17:54:34.417037Z","iopub.status.idle":"2025-03-14T17:54:34.441949Z","shell.execute_reply.started":"2025-03-14T17:54:34.417009Z","shell.execute_reply":"2025-03-14T17:54:34.440607Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def align_to_reference(coords):\n    aligned = coords.copy().astype(float)\n#   reference search\n    for k in range(len(aligned)-3):\n        if np.isnan(aligned[k:k+3]).sum() == 0: break\n#   Translate first atom to origin\n    aligned -= aligned[k]\n    \n    if len(coords) >= 2:\n#       Rotate second atom to x-axis\n        aligned = aligned@givens_rotation(aligned, k+1, 1, 0)\n        aligned = aligned@givens_rotation(aligned, k+1, 2, 0)\n        \n    if len(coords) >= 3:\n#       Rotate third atom into xy-plane\n        aligned = aligned@givens_rotation(aligned, k+2, 2, 0)\n#   Pointing check\n    if aligned[1,0] < 0:  aligned[:,[0,2]] *= -1\n    if aligned[2,1] < 0:  aligned[:,1:] *= -1\n    \n    return aligned,k","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:34.443046Z","iopub.execute_input":"2025-03-14T17:54:34.443391Z","iopub.status.idle":"2025-03-14T17:54:34.470156Z","shell.execute_reply.started":"2025-03-14T17:54:34.443364Z","shell.execute_reply":"2025-03-14T17:54:34.468914Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#i = np.random.randint(len(all_xyz))\ni = 291#639,675,291,705 Intereasting structures\nxyz = all_xyz[i].copy()\naligned,k = align_to_reference(xyz)\nbonds, angles, torsions = calculate_internal_coordinates(aligned[k:])\nreconstructed_coords = reconstruct_from_internal(bonds, angles, torsions)\nrmsd = check_reconstruction(torch.tensor(all_xyz[i][k:]).float(), torch.tensor(reconstructed_coords).float(),display=True)\nprint(f\"Reconstruction RMSD: {rmsd:.6f} Å\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T17:54:34.471518Z","iopub.execute_input":"2025-03-14T17:54:34.471894Z","iopub.status.idle":"2025-03-14T17:54:35.257447Z","shell.execute_reply.started":"2025-03-14T17:54:34.471865Z","shell.execute_reply":"2025-03-14T17:54:35.256052Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"all_k = []\nall_bonds = []\nall_angles = []\nall_torsions = []\nfor xyz in tqdm(all_xyz):\n    xyz,k = align_to_reference(xyz)\n    bonds, angles, torsions = calculate_internal_coordinates(xyz[k:])\n    all_k.append(k)\n    all_bonds.append(bonds)\n    all_angles.append(angles)\n    all_torsions.append(torsions)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T18:06:19.726713Z","iopub.execute_input":"2025-03-14T18:06:19.727103Z","iopub.status.idle":"2025-03-14T18:06:39.488166Z","shell.execute_reply.started":"2025-03-14T18:06:19.727070Z","shell.execute_reply":"2025-03-14T18:06:39.486430Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Some EDA","metadata":{}},{"cell_type":"code","source":"# Bond distances AB\nplt.hist(np.concatenate([np.array(b) for b in all_bonds]),100)[2]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T18:06:39.491460Z","iopub.execute_input":"2025-03-14T18:06:39.491927Z","iopub.status.idle":"2025-03-14T18:06:39.976717Z","shell.execute_reply.started":"2025-03-14T18:06:39.491890Z","shell.execute_reply":"2025-03-14T18:06:39.975285Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Angles ABC\nplt.hist(np.concatenate([np.array(a) for a in all_angles]),100)[2]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T18:06:46.617482Z","iopub.execute_input":"2025-03-14T18:06:46.618187Z","iopub.status.idle":"2025-03-14T18:06:47.265434Z","shell.execute_reply.started":"2025-03-14T18:06:46.618101Z","shell.execute_reply":"2025-03-14T18:06:47.264012Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Torsion angles ABCD\nplt.hist(np.concatenate([np.array(t) for t in all_torsions]),100)[2]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-03-14T18:01:37.112862Z","iopub.execute_input":"2025-03-14T18:01:37.113372Z","iopub.status.idle":"2025-03-14T18:01:37.500551Z","shell.execute_reply.started":"2025-03-14T18:01:37.113331Z","shell.execute_reply":"2025-03-14T18:01:37.499020Z"}},"outputs":[],"execution_count":null}]}