{
"cells": [
{
"cell_type": "markdown",
"id": "31536271",
"metadata": {},
"source": [
"# Vivarium E. coli Processes\n",
"\n",
"This notebook demonstrates how the processes work individually. For each process, we will:\n",
"\n",
"1. Load in the process and parameters\n",
"2. Plot a **toplogy** diagram \n",
" - The topology is a network that demonstrates how a process connects to its stores (which hold state variables).\n",
"3. Display the **ports schema**\n",
" - The port schema defines a systems ports (top-level keys), and the expected behavior of molecules under that port (its *schema*)\n",
" - `*` is a wild card, specifies the schema of everything that can go into the port\n",
"4. Simulate the process\n",
"5. Demonstrate distinct features of that process\n",
" \n",
"**Miscellaneous notes/ideas:**\n",
"- consider adding interactive widgets (plotly) - users can click boxes to choose which molecules to plot"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "1a874047",
"metadata": {},
"outputs": [],
"source": [
"# Make sure notebook runs out of vivarium-ecoli directory\n",
"import sys, os\n",
"\n",
"# get the path to the notebook, and change working directory\n",
"notebook_path = sys.path[0][:sys.path[0].index('notebooks')]\n",
"sys.path.append(notebook_path)\n",
"os.chdir(sys.path[-1])\n",
"cwd = os.getcwd()"
]
},
{
"cell_type": "markdown",
"id": "da299ec6",
"metadata": {
"tags": []
},
"source": [
"## Load the required components"
]
},
{
"cell_type": "markdown",
"id": "99b85cd0",
"metadata": {},
"source": [
"### import modules"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "3788b393",
"metadata": {},
"outputs": [],
"source": [
"from vivarium.core.process import Process\n",
"from vivarium.core.store import Store\n",
"from vivarium.core.engine import Engine, pp\n",
"from vivarium.core.composition import simulate_process, simulate_composite\n",
"from vivarium.plots.topology import plot_topology\n",
"from vivarium.plots.simulation_output import plot_variables\n",
"from ecoli.processes.registries import topology_registry\n",
"import ecoli\n",
"import copy"
]
},
{
"cell_type": "markdown",
"id": "c5af7179",
"metadata": {},
"source": [
"### Load sim_data\n",
"\n",
"This unpickles and `sim_data` object, which holds model parameters from wcEcoli.\n",
"`LoadSimData` includes functions to retrieve individual processes' parameters, which can be modified and passed into their respective process models."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "c37b5e2b",
"metadata": {},
"outputs": [],
"source": [
"from ecoli.library.sim_data import LoadSimData\n",
"\n",
"SIM_DATA_PATH = 'reconstruction/sim_data/kb/simData.cPickle'\n",
"\n",
"load_sim_data = LoadSimData(\n",
" sim_data_path=SIM_DATA_PATH,\n",
" seed=0)"
]
},
{
"cell_type": "markdown",
"id": "0092ca88",
"metadata": {},
"source": [
"### Get initial state snapshot\n",
"\n",
"`initial_state` is a dict with the initial state of the system -- a snapshot saved from wcEcoli."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "22b131f2",
"metadata": {},
"outputs": [],
"source": [
"from ecoli.composites.ecoli_master import get_state_from_file\n",
"\n",
"INITIAL_STATE_PATH = 'data/wcecoli_t1000.json'\n",
"\n",
"initial_state = get_state_from_file(path=INITIAL_STATE_PATH)"
]
},
{
"cell_type": "markdown",
"id": "0414f33e",
"metadata": {},
"source": [
"### Helper functions for executing the notebook"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "1761fea1",
"metadata": {},
"outputs": [],
"source": [
"schema_keys = Store.schema_keys\n",
"\n",
"def make_port_printout(ports_schema, depth=0, schema_show=5, filler_size=5):\n",
" print_dict = ''\n",
" filler = filler_size * ' '\n",
" for port, schema in ports_schema.items():\n",
" if isinstance(schema, dict):\n",
" schemavars = list(schema.keys())\n",
" if any(var in schemavars for var in schema_keys):\n",
" print_schema = ''\n",
" for k, v in schema.items():\n",
" print_schema += f'{(depth+1) * filler} {k}: {v}\\n'\n",
" print_dict += f'{depth * filler}{port}:\\n{print_schema}\\n'\n",
" else:\n",
" schema_items = schema.items()\n",
" first_schema = dict(list(schema_items)[:schema_show])\n",
" next_print = make_port_printout(first_schema, depth+1)\n",
" print_dict += f'{port}:\\n{next_print}\\n'\n",
" if len(schema) > schema_show:\n",
" print_dict += f'{(depth+1) * filler}'\n",
" print_dict += f'... skipping {len(schema)-schema_show} schema entries ...'\n",
" print_dict += f'\\n\\n'\n",
" else:\n",
" print_dict += f'{filler}{schema}\\n'\n",
" return print_dict\n",
"\n",
"def find_increasing(d):\n",
" for key, value in d.items():\n",
" if value[-1] > value[0]:\n",
" return {key: value}\n",
" \n",
"def find_decreasing(d):\n",
" for key, value in d.items():\n",
" if value[-1] < value[0]:\n",
" return {key: value}"
]
},
{
"cell_type": "markdown",
"id": "bed93e79",
"metadata": {},
"source": [
"## Complexation\n",
" \n",
"[Complexation Documentation](https://covertlab.github.io/vivarium-ecoli/reference/api/ecoli.processes.complexation.html)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "f6f95f82",
"metadata": {},
"outputs": [],
"source": [
"from ecoli.processes.complexation import Complexation\n",
"\n",
"# load in parameters\n",
"cplx_config = load_sim_data.get_complexation_config()\n",
"cplx_config['time_step'] = 0.01\n",
"\n",
"# initialize process\n",
"complexation = Complexation(cplx_config)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "4431d567",
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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26a4uDgtWrRIkrR3716NHj1aXbp0UWJiooYMGaKlS5cGnn/LLbfowIEDeuqpp2SMCcx8q6l7YcmSJRo0aJBiY2N1ySWX6Oc//7kqTzTKzMzUCy+8oIkTJyolJUUZGRn6xS9+0ah2QStlreUSSZdnUmzg4peTk2NTU1Pt73//e5ubm2vXrVtnX3nlFXvmzBnbtWtXO3r0aLtt2za7evVq27dvXztmzJjAa8ePH2+Tk5PtxIkT7a5du+ybb75pjTF25MiRNicnx+7evdvOnTvXSrIbN24MvE6STU9Pt7/97W/t7t277QsvvGCNMXbDhg1VnrNo0SJrrbVFRUW2b9++dvz48Xbr1q12165d9h//8R9tjx49bFFRkbXW2uXLl9uYmBi7bt06W1xcbLOysuyECRMCy/v000/t3Llz7bZt22xubq594YUXbExMjN21a5e11toTJ07YjIwM+7Of/czm5eXZvLw8a62177//vpVkjx07Zq21duPGjTYqKsr+7Gc/s7t377ZvvPGGTUxMtL/85S8D6+rZs6dNT0+3v/rVr2xubq795S9/aSXZdevW2Sqqtof3fx9cQn7xvAAuji/VQrewsNDGxsbauXPn2urmzZtnU1JSbEFBQeC+igDKzc211vpCNyMjw5aVlQWeM3ToUJudnV1lWT179rS/+MUvArcl2UcffbTKc0aMGGHHjh1b5TkVofv73//e9unTx5aXlwceLysrs+np6fZPf/pT4L7HH3/c9urVy06YMMH27t3bFhYWXvR7VTZs2DD7/PPP11pn5d+5InS/+93v2ltvvbXKc5555hnbvXv3Kst56KGHqjynT58+VdbleyGhG2kXuhci3M6dO3Xu3DmNGDHiosd27dql7OxsJScnB+4bPny4oqKitHPnzsB9WVlZatOmTeB2586dNXDgwCrL6ty5s77++usq91133XUX3a683Mo2bdqkffv2KTk5WUlJSUpKSlJqaqpOnTqlvXv3Bp43Y8YMtW3bVgsWLNDChQuVlJQUeKyoqEhPP/20srKylJaWpqSkJG3cuFFffvllXW/RRXbt2qXrr7++yn033HCDDh8+rIKCgsB92dnZVZ7TrVu3i94DRB4OpKFJKp8oPCYm5qLHarqv4qTlTVFeXq4rrrhC//3f/33RY+np6YGf9+/fr4MHD8oYoy+++ELDhg0LPDZ58mQtX75cM2fOVN++fZWQkKBx48bp/PnzTa6ruvrel+a8B2gd2NONcAMGDFBsbKxWrVpV42Pbt29XYWFh4L5169apvLxcAwYMaPa6P/7444tu17bcIUOGaM+ePerQocNFJzyvCN3S0lJ997vf1X333aeZM2fqscceq7IX++GHH2rcuHF64IEHlJ2drYyMjCp7yZJvpMKFCxfqrLu2k6dnZGRU+VQA1ITQjXDJycl6/PHHNXXqVM2fP1979+7V+vXrNXfuXI0dOzawN7h9+3atXbtWEydO1JgxY9SnT59mr3vJkiV69dVXlZubqxdffFGrVq3SE088UeNzx44dq86dO2v06NFas2aN9u3bp7Vr1+rJJ58MjGD493//dx07dkxz587V448/rmHDhmncuHGBvct+/frp7bff1ubNm7V9+3Z973vfU0lJSZX1ZGZm6oMPPtDhw4drnQzx5JNPas2aNZo2bZo+//xzLVy4ULNmzdLTTz/d7PcErR+hC7344ouaMmWKnn/+eQ0YMEAPPPCADh06pISEBK1YsUIFBQW65pprNHr0aF133XV67bXXgrLeadOmafHixcrOztbcuXM1f/58XX311TU+NyEhQWvXrtWll16qBx98UP3799f48eN16tQppaWlac2aNZo1a5YWLFigdu3ayRijP/zhD9q5c2dguNrLL7+sTp066cYbb9Tdd9+ta6+9NvAtxBWee+45HTx4UL1791bHjh1rrGXIkCFatGiRFi9erIEDByonJ0c5OTmBbyUG6sJJzCNNmJzE3BijRYsW6dvf/rZnNYQFTmIecdjTBQCHCF0AcIghY/AE3VqIVOzpAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBDhC4AOEToAoBD0V4XAA9NS/W6AiDisKcbec54XQBqRLtECEI38kwTG3i4OSNfuyACGGut1zUAMsbkSHpR0ivW2p8EcbmPSPq9pEXW2v8TrOUCTcWeLsLF1f7rDUFe7vpqywc8RegiXFSE4vo6n9V4uyQVSco0xnQM8rKBRiN04TljTBdJl0gq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rm7w4MEaPHiwpkyZorvvvlt//OMfW13oSrX/nm3bttWFCxeqPPfKK6+UtVZHjx7Vrbfe2uR1NrQNahIdHa3bbrtNt912m5599ll16tRJS5cu1Q9/+MMm14PG4UBamBg7dqwSEhI0btw4bd++XWvXrtXEiRM1ZsyYWjeuZ599Vi+99JJmz56t3bt3a8eOHVqwYIFefPFFSdKIESM0bNgwPfDAA1qxYoX27dunlStX6p133pEkDRs2TImJiZo6dar27NmjxYsXBw7C1WbKlClav369fvSjH2nLli3as2ePli5dqokTJ0qS9u3bp5ycHK1bt04HDhzQ+++/r23btl30kbqlq+/3zMzMVElJiVauXKnjx4+ruLhY/fr109ixYzVhwgS99dZb+uKLL7Rx40bNnDlTS5YsafC662uD2ixdulRz5szRli1bdODAAb355psqLCwMavcT6kfohomEhAStWLFCBQUFuuaaazR69Ghdd911eu2112p9zaOPPqrXXntNr7/+ugYPHqwbb7xR8+bNU69evST5ug/+8pe/6Prrr9f3vvc9DRgwQI8//rjOnz8vydcvuXDhQq1cuVKDBg3SvHnz9Pzzz9dZZ3Z2ttauXav9+/fr5ptv1uDBgzV16lR17tw58Ht8/vnnevDBB9WvXz+NHz9eY8eO1ZQpU4L0ToWH+n7P4cOH60c/+pG+853vqGPHjnrppZck+bokHn74YT399NPq37+/7rnnHq1du1Y9e/Zs8Lrra4PatGvXTu+8845uv/129e/fXzNnztR//ud/6sYbb2z6G4FGM9X79CBl5iw7IqmrJH3y0xHqnBLncUWR7Zqf/1VfF56ruNl9//RRRyo/TnuFl/raK9KxpwsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoQsADhG6AOAQoVuPC+XW6xIiXknphQY/l/byXmPaKxIRujU7U/HDlMXbdOT0WS9riViFJaV67t2dKigpq7jLSiqu4am0VxhoRHtFNEK3Zq9V/PBB7nGNmLVGs1d+roMn+ftx4WTReb3x8QHdNmuNXvvbvsoPLd4/fdTpGl5Ce3moCe0V0Yy1fByrLjNnWbSkeZIerv5YVtcUjby8i0YO7KzLOifLGOO+wFbo8Omzeu+zo1rx2VGt33dSNfQSrJU0Zv/0USeqP0B7udec9op0hG4dMnOW3STpV5Kya3o8NT5G2RmpGtQ91Xed0U7dUuPYsOtx4sw5bT+cr+2H8rXNf320oKS2px+R9JSk/9o/fVSdf6y0V2iEqr0iFaFbD/9e1HckPSTpdklt63p+u4QY9UxPUEZagjLS433XafG6xH8dF9PGRdmeKr1QrrzTJTp0qlgHTxXr0KmzOnTqrA6e9N3+quBcQxazTtJiSfP2Tx91pr4nV6C9Gs/L9opEhG4jZOYsS5H0LUlj5Nug0xq7jI7JsYGNukNSrJLjopUcF62k2GglxUUrOS5GSbHRVe5PbButqCj3e2PWWp0tvaAzJWUqKCnTmXNlOlNSpsKSUhUGfi7TmXOlOllUqkP+DTYv/2xNHzfrc1bSh5KWSPqf/dNH5TW3ftqrZbVXpCB0mygzZ5mR1EvSVZKGVrpODfa6jJGS2vo28qobextFRzX/WOgFa1V8rmKD9F0XlpTqzLmypmyMDVEiaaukjZUuf98/fVRZna9qBtqrWZy3V2tG6AZRZs6yKEndJGX6L72qXfeQ1Po/r/ockbRP0v4arg/unz6q1KvCKtBeVYR9e7UWhK5D/v7G7vpmw24vKaXSJbWWn5M8KLfCWUkF/kt+HT+flnRAvo30wP7poxrUERjOaC+EAqHbAmTmLGsjKVkXb9wpCs6emJVUqIs3zgL2cBqP9kJdCF0AcIgZaQDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDgEKELAA4RugDg0P8HwooEFnU1xBcAAAAASUVORK5CYII=\n",
"text/plain": [
"