{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# SPARCED Model Creation\n", "\n", "This file transforms the input files into Antimony and SBML model files. Here, we do not execute any simulation. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### These are the \"default\" input file names\n", "\n", "They are structured, tab-separated text files, located in the \"input files\" folder by default." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Input file name definitions\n", "fileComps = 'Compartments.txt' # input\n", "fileSpecies = 'Species.txt' # input\n", "fileStoic = 'StoicMat.txt' # input\n", "fileRatelaws = 'Ratelaws.txt' # input\n", "fileParamsOut = 'ParamsAll.txt' # output: Lists all parameter names, rxn names, values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Import required packages and scripts" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sys\n", "import os\n", "sys.path.append(os.getcwd()[0:os.getcwd().rfind('/')]+'/bin')\n", "\n", "import libsbml\n", "import importlib\n", "import amici\n", "import numpy as np\n", "import re\n", "import pandas as pd\n", "from antimony import *\n", "from modules.copyDir import copyDirectory\n", "\n", "# Optional packages to import\n", "import amici.plotting\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Copy the input files from the \"input_files\" folder to the current folder\n", "\n", "This step can be skipped if the input files are already in the working directory" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#copy input files over to current directory\n", "current_dir = os.getcwd()\n", "input_data_folder = current_dir[0:current_dir.rfind('/')+1]+'input_files'\n", "copyDirectory(input_data_folder, os.getcwd()+\"/\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Define the model (file) name here\n", "\n", "First, the Antimony model file will be created using the input files. Users can add information per the antimony file structure. A default explanation for the SPARCED model is inserted here." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Antimony model name and information text\n", "fileModel = open('SPARCED.txt','w') # file name\n", "fileModel.write(\"# PanCancer Model by Birtwistle Lab \\n\") # some explanation\n", "fileModel.write(\"model SPARCED()\\n\") # model name" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Define the model SBML file name here\n", "\n", "The SBML file will be created using the Antimony file. Define its name here. \n", "For consistency, we use the same name for the Antimony file, SBML file, and the model name." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# SBML file name\n", "sbml_file = 'SPARCED.xml'\n", "# Name of the model that will also be the name of the python module\n", "model_name = sbml_file[0:-4] \n", "# Directory to which the generated model code is written\n", "model_output_dir = model_name \n", "# The AMICI package will create this folder while compiling the model, refer to AMICI github page for more details" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Input file processing\n", "\n", "Below are steps of input file processing and writing the Antimony file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### First, read-in the \"Compartments\" and \"Volumes\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Initializing compartment and volume lists\n", "compartments = []\n", "volumes = []\n", "\n", "# Create/write compartments\n", "compartment_sheet = np.array([np.array(line.strip().split(\"\\t\")) for line in open(fileComps)])\n", "\n", "#read in each line minus the header row of compartments file\n", "for row in compartment_sheet[1:]:\n", " compartments.append(row[0])\n", " volumes.append(row[1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Set (write) the Compartments's names and assing the Volume values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fileModel.write(\"\\n # Compartments and Species:\\n\") # Antimony Compartments/Species module title\n", "for idx in range(len(compartments)):\n", " compName = compartments[idx]\n", " fileModel.write(\" Compartment %s; \" % (compName))\n", "fileModel.write(\"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Second, read-in the \"Species\" and related information" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Write species and assign compartments\n", "species_sheet = np.array([np.array(line.strip().split(\"\\t\")) for line in open('Species.txt', encoding='latin-1')])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#read in each line minus the header row of species file\n", "species_compartments = [] # Create a list of \"home\" compartments for each species in the model\n", "for row in species_sheet[1:]:\n", " species_compartments.append(row[1]) \n", "species_compartments = np.array(species_compartments)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Write the species names and their compartments in the Antimony format" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Write each species to model txt file\n", "fileModel.write(\"\\n\")\n", "for idx,val in enumerate(species_sheet[1:]):\n", " fileModel.write(\" Species \")\n", " fileModel.write(\"%s in %s\" % (val[0], species_compartments[idx]))\n", " fileModel.write(\";\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Third, read-in the \"Reactions\" and \"StoicMat\" files to create reactions of the model" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Write reactions\n", "fileModel.write(\"\\n\\n # Reactions:\\n\") # Antimony Reactions module title\n", "\n", "#reads in file from excel and gets rid of first row and column (they're data labels)\n", "stoic_sheet = np.array([np.array(line.strip().split(\"\\t\")) for line in open('StoicMat.txt')])\n", "\n", "#creates associated ratelaw data list\n", "ratelaw_sheet = np.array([np.array(line.strip().split(\"\\t\")) for line in open('Ratelaws.txt')])\n", "ratelaw_data = np.array([line[1:] for line in ratelaw_sheet[1:]])\n", "\n", "#gets first column minus blank space at the beginning, adds to stoic data list\n", "stoic_columnnames = stoic_sheet[0]\n", "stoic_rownames = [line[0] for line in stoic_sheet[1:]]\n", "stoic_data = np.array([line[1:] for line in stoic_sheet[1:]])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Create the reations of the model based on the stoichiometric input and (if available) ratelaw input" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# builds the important ratelaw+stoic lines into the txt file \n", "paramnames = []\n", "paramvals = []\n", "paramrxns = []\n", "paramidxs = []\n", "for rowNum, ratelaw in enumerate(ratelaw_data):\n", " reactants = []\n", " products = []\n", " formula=\"k\"+str(rowNum+1)+\"*\"\n", "\n", " for i, stoic_rowname in enumerate(stoic_rownames):\n", " stoic_value = int(stoic_data[i][rowNum])\n", " if stoic_value < 0:\n", " for j in range(0,stoic_value*-1):\n", " reactants.append(stoic_rowname)\n", " formula=formula+stoic_rowname+\"*\"\n", " elif stoic_value > 0:\n", " for j in range(0,stoic_value):\n", " products.append(stoic_rowname)\n", "\n", " if \"k\" not in ratelaw[1]:\n", " # the mass-action formula\n", " formula=formula[:-1]\n", " #the parameter\n", " paramnames.append(\"k\"+str(rowNum+1))\n", " paramvals.append(np.double(ratelaw[1]))\n", " paramrxns.append(ratelaw_sheet[rowNum+1][0])\n", " paramidxs.append(int(0))\n", " else:\n", " # specific formula (non-mass-action)\n", " formula = ratelaw[1]\n", " j = 1\n", " params = np.genfromtxt(ratelaw[2:], float) # parameters\n", " params = params[~np.isnan(params)]\n", " if len(params) == 1:\n", " paramnames.append(\"k\"+str(rowNum+1)+\"_\"+str(j))\n", " paramvals.append(float(ratelaw[j+1]))\n", " paramrxns.append(ratelaw_sheet[rowNum+1][0])\n", " paramidxs.append(int(0))\n", " pattern = 'k\\D*\\d*'\n", " compiled = re.compile(pattern)\n", " matches = compiled.finditer(formula)\n", " for ematch in matches:\n", " formula = formula.replace(ematch.group(),paramnames[-1])\n", " else:\n", " for q,p in enumerate(params):\n", " paramnames.append(\"k\"+str(rowNum+1)+\"_\"+str(j))\n", " paramvals.append(float(ratelaw[j+1]))\n", " paramrxns.append(ratelaw_sheet[rowNum+1][0])\n", " paramidxs.append(q)\n", " pattern1 = 'k(\\D*)\\d*'+'_'+str(j)\n", " compiled1 = re.compile(pattern1)\n", " matches1 = compiled1.finditer(formula)\n", " for ematch in matches1:\n", " formula = formula.replace(ematch.group(),paramnames[-1])\n", " j +=1\n", " if ratelaw[0] == 'Cytoplasm':\n", " valcomp = 5.25e-12\n", " elif ratelaw[0] == 'Extracellular':\n", " valcomp = 5.00e-5\n", " elif ratelaw[0] == 'Nucleus':\n", " valcomp = 1.75e-12\n", " elif ratelaw[0] == 'Mitochondrion':\n", " valcomp = 3.675e-13\n", " #don't include reactions without products or reactants\n", " if products == [] and reactants == []:\n", " pass\n", " else:\n", " fileModel.write(\" %s: %s => %s; (%s)*%.6e;\\n\" % (stoic_columnnames[rowNum], \" + \".join(reactants), \" + \".join(products), formula, valcomp))\n", "\n", "# Export parameters for each reaction, with corresponding order within the ratelaw and its value\n", "params_all = pd.DataFrame({'value':paramvals,'rxn':paramrxns,'idx':paramidxs},index=paramnames)\n", "params_all.to_csv(fileParamsOut,sep='\\t',header=True, index=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Next, we set the \"Initial Conditions\" for Compartments, Species, and Parameters" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The Compartment initial conditions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Write compartment ICs\n", "fileModel.write(\"\\n # Compartment initializations:\\n\")\n", "for idx in range(len(compartments)):\n", " fileModel.write(\" %s = %.6e;\\n\" % (compartments[idx], np.double(volumes[idx])))\n", " fileModel.write(\" %s has volume;\\n\" % (compartments[idx]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The Species initial conditions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Write species ICs\n", "fileModel.write(\"\\n # Species initializations:\\n\")\n", "for idx, val in enumerate(species_sheet[1:]):\n", " fileModel.write(\" %s = %.6e;\\n\" % (val[0],np.double(val[2])))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The Parameter (of reactions) initial conditions" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Write parameter ICs\n", "fileModel.write(\"\\n # Parameter initializations:\\n\")\n", "count = 0\n", "for param in paramnames:\n", " fileModel.write(\" %s = %.6e;\\n\" % (param, np.double(paramvals[count])))\n", " count += 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Other declarations supported by Antimony" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Write other declarations\n", "constantVars = ['Cytoplasm','Extracellular','Nucleus','Mitochondrion']\n", "\n", "fileModel.write(\"\\n # Other declarations:\\n\")\n", "fileModel.write(\" const\")\n", "for constVar in constantVars[:-1]:\n", " fileModel.write(\" %s,\" % (constVar))\n", "#last item in row needs semicolon\n", "fileModel.write(\" %s;\\n\" % (constantVars[-1]))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The Unit Definitions\n", "\n", "We define the time, volume, and substance units" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Write unit definitions\n", "fileModel.write(\"\\n # Unit definitions:\")\n", "fileModel.write(\"\\n unit time_unit = second;\")\n", "fileModel.write(\"\\n unit volume = litre;\")\n", "fileModel.write(\"\\n unit substance = 1e-9 mole;\")\n", "fileModel.write(\"\\n unit nM = 1e-9 mole / litre;\")\n", "fileModel.write(\"\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### End of the Antimony file, close the file" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# End the model file\n", "fileModel.write(\"\\nend\")\n", "# Close the file\n", "fileModel.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The Antimony file import and conversion to SBML format\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# load model and convert to SBML\n", "if loadFile(\"SPARCED.txt\") == 1:\n", " print(\"Success loading antimony file\")\n", "else:\n", " print(\"Failed to load antimony file\")\n", " exit(1)\n", "\n", "if writeSBMLFile(\"SPARCED.xml\",\"SPARCED\") == 1:\n", " print(\"Success converting antimony to SBML\")\n", "else:\n", " print(\"Failure converting antimony to SBML\")\n", " exit(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The initial SBML file is imported again, to add annotations for the Compartments and Species\n", "\n", "Currently, the Antimony file format does not support Annotation definitions. So, we re-process the SBML file and add Annotations for the Compartments (GO terms) and Species (HGNC or ENSEMBL identifiers)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Import the SBML file and get the model handle" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# create interaction components\n", "sbml_reader = libsbml.SBMLReader()\n", "sbml_doc = sbml_reader.readSBML(sbml_file)\n", "sbml_model = sbml_doc.getModel()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The Species annotations are set using the last column of the Species input file" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Set species annotations\n", "for idx,row in enumerate(species_sheet[1:]):\n", " Annot=\"\"\n", " for col in range(4,(len(row))):\n", " aa=str(row[col].strip())\n", " if aa==\"nan\" or aa == \"\":\n", " break\n", " else:\n", " Annot=Annot+\" \"+row[col]\n", " sbml_model.getSpecies(row[0]).setAnnotation(Annot)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### The Compartment annotations are set using the last column of the Compartments input file" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Set compartment annotations\n", "for row in compartment_sheet[1:]:\n", " sbml_model.getCompartment(row[0]).setAnnotation(row[2])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Export the finalized (annotated) SBML file" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Write with the same name or use the next section instead of below lines\n", "writer = libsbml.SBMLWriter()\n", "writer.writeSBML(sbml_doc, sbml_file)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The Model Compilation" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Import the SBML file and compile using the AMICI package" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# prepares to use interaction components to synthesize model\n", "sys.path.insert(0, os.path.abspath(model_output_dir))\n", "model_name = sbml_file[0:-4]\n", "model_output_dir = model_name\n", "\n", "sbml_reader = libsbml.SBMLReader()\n", "sbml_doc = sbml_reader.readSBML(sbml_file)\n", "sbml_model = sbml_doc.getModel()\n", "\n", "# Create an SbmlImporter instance for our SBML model\n", "sbml_importer = amici.SbmlImporter(sbml_file)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Setting AMICI converter arguments\n", "\n", "We set all the rate parameters as constant for faster compilation. It is also a good idea to define \"Observables\" for easier comparison to experimental data. An observable in our model corresponds to the totality of a protein sepcies, including all of its post-translationally modified and unmodified states, corrected for compartmental volume differences and reported based on cytoplasmic volume. We defined 102 observables using the \"Observables.txt\" input file in `AMICI` required format." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#sets important constants for model build\n", "constantParameters = [params.getId() for params in sbml_model.getListOfParameters()]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# creates observables.\n", "ObsMat = pd.read_csv('Observables.txt', sep='\\t',header=0, index_col=0)\n", "Vc = float(compartment_sheet[compartment_sheet[:,0]=='Cytoplasm',1])\n", "\n", "species_names = np.array([species_sheet[i][0] for i in range(1,len(species_sheet))])\n", "Vol_species = np.array([species_sheet[i][1] for i in range(1,len(species_sheet))])\n", "Vol_species = [float(compartment_sheet[compartment_sheet[:,0]==Vol_species[i],1][0]) for i in range(len(Vol_species))]\n", "Vol_species = pd.Series(Vol_species, index=species_names)\n", "\n", "formula_obs = []\n", "for obs in ObsMat.columns:\n", " sp_obs = ObsMat.index[np.nonzero(np.array(ObsMat.loc[:,obs]>0))[0]]\n", " sp_obs_id = np.nonzero(np.array(ObsMat.loc[:,obs]>0))[0]\n", " Vr = Vol_species/Vc\n", " Vf = Vr*ObsMat.loc[:,obs].values\n", " if len(sp_obs) == 1:\n", " formula_i = sp_obs[0]+'*'+str(Vf[sp_obs_id][0])\n", " elif len(sp_obs) == 2:\n", " formula_i = str(sp_obs[0]+'*'+str(Vf[sp_obs_id][0])+'+'+sp_obs[1]+'*'+str(Vf[sp_obs_id][1]))\n", " elif len(sp_obs) > 2:\n", " formula_i = ''\n", " for j in range(len(sp_obs)-1):\n", " formula_i = formula_i+sp_obs[j]+'*'+str(Vf[sp_obs_id][j])+'+'\n", " formula_i = formula_i+str(sp_obs[-1])+'*'+str(Vf[sp_obs_id][-1])\n", " formula_obs.append(formula_i)\n", "\n", "observables = {}\n", "obs_names = list(ObsMat.columns)\n", "for i in range(len(obs_names)):\n", " observables[obs_names[i]] = {}\n", " observables[obs_names[i]]['formula'] = formula_obs[i]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Compile the model and create required C++ dependencies for simulation. \n", "\n", "Creates a sub-folder with the same name as the model (i.e. SPARCED)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The actual compilation step by AMICI, takes a while to complete for large models\n", "sbml_importer.sbml2amici(model_name,\n", " model_output_dir,\n", " verbose=False,\n", " observables=observables,\n", " constantParameters=constantParameters)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The model creation is now complete! Enjoy..." ] } ], "metadata": { "file_extension": ".py", "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.9" }, "mimetype": "text/x-python", "name": "python", "npconvert_exporter": "python", "pygments_lexer": "ipython3", "version": 3 }, "nbformat": 4, "nbformat_minor": 2 }