def main(args): """usage: convertFbcToCobra.py input-filename output-filename """ if len(args) != 3: print(main.__doc__) sys.exit(1) infile = args[1] outfile = args[2] if not os.path.exists(infile): print("[Error] %s : No such file." % infile) sys.exit(1) reader = libsbml.SBMLReader() writer = libsbml.SBMLWriter() sbmldoc = reader.readSBML(infile) if sbmldoc.getNumErrors() > 0: if sbmldoc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. sbmldoc.printErrors() elif sbmldoc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. sbmldoc.printErrors() else: # Handle other error cases here. sbmldoc.printErrors() # sys.exit(1) # strip non-FBC plugins for p_ in range(sbmldoc.getNumPlugins()): if sbmldoc.getPlugin(p_).getPackageName() != 'fbc': props = libsbml.ConversionProperties() props.addOption( "stripPackage", True, "Strip SBML Level 3 package constructs from the model") props.addOption("package", sbmldoc.getPlugin(p_).getPackageName(), "Name of the SBML Level 3 package to be stripped") if sbmldoc.convert(props) != libsbml.LIBSBML_OPERATION_SUCCESS: print("[Error] Failed to remove package: {}".format( sbmldoc.getPlugin(p_).getPackageName())) # convert to L2 props = libsbml.ConversionProperties() props.addOption("convert fbc to cobra", True, "Convert FBC model to Cobra model") result = sbmldoc.convert(props) if result != libsbml.LIBSBML_OPERATION_SUCCESS: print("[Error] Conversion failed... (%d)" % result) sys.exit(1) writer.writeSBML(sbmldoc, outfile) print("[OK] converted file %s to %s" % (infile, outfile))
def main (args): """usage: flattenModel.py [-p] input-filename output-filename -p : list unused ports """ if len(args) != 4 and len(args) != 3 : print(main.__doc__) sys.exit(1) leavePorts = False if len(args) == 3: infile = args[1] outfile = args[2] elif len(args) == 4: if args[1] != "-p": print(main.__doc__) sys.exit(1) else: leavePorts = True infile = args[2] outfile = args[3] if not os.path.exists(infile): print("[Error] %s : No such file." % (infile)) sys.exit(1) reader = libsbml.SBMLReader() writer = libsbml.SBMLWriter() sbmldoc = reader.readSBML(infile) if sbmldoc.getNumErrors() > 0: if sbmldoc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. sbmldoc.printErrors() elif sbmldoc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. sbmldoc.printErrors() else: # Handle other error cases here. sbmldoc.printErrors() sys.exit(1) # Create the converter options props = libsbml.ConversionProperties() props.addOption("flatten comp", True, "flatten comp") props.addOption("leavePorts", leavePorts, "unused ports should be listed in the flattened model") # do conversion result = sbmldoc.convert(props) if (result != libsbml.LIBSBML_OPERATION_SUCCESS): sbmldoc.printErrors() print("[Error] Conversion failed... ("+ str(result) + ")") sys.exit(1) writer.writeSBML(sbmldoc, outfile) print("Flat model written to %s" % (outfile))
def promote_params(model, document): """Change parameters to global, so that they are preserved when reactions change.""" convProps = libsbml.ConversionProperties() convProps.addOption("promoteLocalParameters", True, "Promotes all Local Parameters to Global ones") if (document.convert(convProps) != libsbml.LIBSBML_OPERATION_SUCCESS): raise SystemExit("Error promoting local parameters to global.")
def convert_functions(model, document): """Replace functions with formulas.""" config = libsbml.ConversionProperties() if config != None: config.addOption('expandFunctionDefinitions') status = document.convert(config) if status != libsbml.LIBSBML_OPERATION_SUCCESS: print('Error: function conversion failed:') document.printErrors()
def main(args): """usage: stripPackage.py input-filename package-to-strip output-filename """ if len(args) != 4: print(main.__doc__) sys.exit(1) infile = args[1] package = args[2] outfile = args[3] if not os.path.exists(infile): print("[Error] %s : No such file." % (infile)) sys.exit(1) reader = libsbml.SBMLReader() writer = libsbml.SBMLWriter() sbmldoc = reader.readSBML(infile) if sbmldoc.getNumErrors() > 0: if sbmldoc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. sbmldoc.printErrors() elif sbmldoc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. sbmldoc.printErrors() else: # Handle other error cases here. sbmldoc.printErrors() sys.exit(1) props = libsbml.ConversionProperties() props.addOption("stripPackage", True, "Strip SBML Level 3 package constructs from the model") props.addOption("package", package, "Name of the SBML Level 3 package to be stripped") if (sbmldoc.convert(props) != libsbml.LIBSBML_OPERATION_SUCCESS): print("[Error] Conversion failed...") sys.exit(1) writer.writeSBML(sbmldoc, outfile) print("[OK] stripped package '%s' from %s to %s" % (package, infile, outfile))
def main(args): """usage: flattenArrays.py input-filename output-filename """ if len(args) != 3: print(main.__doc__) sys.exit(1) infile = args[1] outfile = args[2] if not os.path.exists(infile): print("[Error] {} : No such file.".format(infile)) sys.exit(1) reader = libsbml.SBMLReader() writer = libsbml.SBMLWriter() sbmldoc = reader.readSBML(infile) if sbmldoc.getNumErrors() > 0: if sbmldoc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. sbmldoc.printErrors() elif sbmldoc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. sbmldoc.printErrors() else: # Handle other error cases here. sbmldoc.printErrors() sys.exit(1) props = libsbml.ConversionProperties() props.addOption("flatten arrays", True, "flatten arrays") # Optional: validate flattened file ... may take some time # props.addOption("performValidation", True, "perform validation before and after trying to flatten") result = sbmldoc.convert(props) if (result != libsbml.LIBSBML_OPERATION_SUCCESS): print("[Error] Array flattening failed... ({})".format(result)) sys.exit(1) writer.writeSBML(sbmldoc, outfile) print("[OK] Flattened arrays file {} to {}".format(infile, outfile))
def promote_local_variables(doc): """ Promotes local variables in SBMLDocument. :param doc: :return: """ model = doc.getModel() mid = model.id mid = '{}_{}'.format(mid, 'promoted') model.setId(mid) # promote local parameters props = libsbml.ConversionProperties() props.addOption("promoteLocalParameters", True, "Promotes all Local Parameters to Global ones") if doc.convert(props) != libsbml.LIBSBML_OPERATION_SUCCESS: warnings.warn("SBML Conversion failed...") else: print("SBML Conversion successful") return doc
def main(args): """usage: convertFbcToCobra.py input-filename output-filename """ if len(args) != 3: print(main.__doc__) sys.exit(1) infile = args[1] outfile = args[2] if not os.path.exists(infile): print("[Error] %s : No such file." % (infile)) sys.exit(1) reader = libsbml.SBMLReader() writer = libsbml.SBMLWriter() sbmldoc = reader.readSBML(infile) if sbmldoc.getNumErrors() > 0: if sbmldoc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. sbmldoc.printErrors() elif sbmldoc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. sbmldoc.printErrors() else: # Handle other error cases here. sbmldoc.printErrors() #sys.exit(1) props = libsbml.ConversionProperties() props.addOption("convert fbc to cobra", True, "Convert FBC model to Cobra model") result = sbmldoc.convert(props) if (result != libsbml.LIBSBML_OPERATION_SUCCESS): print("[Error] Conversion failed... (%d)" % (result)) sys.exit(1) writer.writeSBML(sbmldoc, outfile) print("[OK] converted file %s to %s" % (infile, outfile))
def main(args): """usage: promoteParameters.py input-filename output-filename """ if len(args) != 3: print(main.__doc__) sys.exit(1) infile = args[1] outfile = args[2] if not os.path.exists(infile): print("[Error] %s : No such file." % (infile)) sys.exit(1) reader = libsbml.SBMLReader() writer = libsbml.SBMLWriter() sbmldoc = reader.readSBML(infile) if sbmldoc.getNumErrors() > 0: if sbmldoc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. sbmldoc.printErrors() elif sbmldoc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. sbmldoc.printErrors() else: # Handle other error cases here. sbmldoc.printErrors() sys.exit(1) props = libsbml.ConversionProperties() props.addOption("promoteLocalParameters", True, "Promotes all Local Parameters to Global ones") if (sbmldoc.convert(props) != libsbml.LIBSBML_OPERATION_SUCCESS): print("[Error] Conversion failed...") sys.exit(1) writer.writeSBML(sbmldoc, outfile) print("[OK] wrote %s" % (package, infile, outfile))
def main (args): """usage: inlineFunctionDefinitions.py input-filename output-filename """ if len(args) != 3: print(main.__doc__) sys.exit(1) infile = args[1] outfile = args[2] if not os.path.exists(infile): print("[Error] %s : No such file." % infile) sys.exit(1) reader = libsbml.SBMLReader() writer = libsbml.SBMLWriter() sbmldoc = reader.readSBML(infile) if sbmldoc.getNumErrors() > 0: if sbmldoc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. sbmldoc.printErrors() elif sbmldoc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. sbmldoc.printErrors() else: # Handle other error cases here. sbmldoc.printErrors() sys.exit(1) props = libsbml.ConversionProperties() props.addOption("expandFunctionDefinitions", True) if sbmldoc.convert(props) != libsbml.LIBSBML_OPERATION_SUCCESS: print("[Error] Conversion failed...") sys.exit(1) writer.writeSBML(sbmldoc, outfile) print("[OK] wrote {}".format(outfile))
def promote_local_variables(doc: libsbml.SBMLDocument, suffix: str = "_promoted") -> libsbml.SBMLDocument: """Promotes local variables in SBMLDocument. Manipulates SBMLDocument in place! :param doc: SBMLDocument :param suffix: str suffix for promoted SBML :return: SBMLDocument with promoted parameters """ model: libsbml.Model = doc.getModel() model.setId(f"{model.id}{suffix}") # promote local parameters props = libsbml.ConversionProperties() props.addOption("promoteLocalParameters", True, "Promotes all Local Parameters to Global ones") if doc.convert(props) != libsbml.LIBSBML_OPERATION_SUCCESS: logger.error(f"Promotion of local parameters failed: {doc}") else: logger.info(f"Promotion of local paramters successful: {doc}") return doc
def writeCode(doc): comp_template = 'model.addCompartment(vol=%s, comp_id=\'%s\');' species_template = 'model.addSpecies(species_id=\'%s\', amt=%s, comp=\'%s\');' param_template = 'model.addParameter(param_id=\'%s\', val=%s, units=\'%s\');' rxn_template = 'model.addReaction(reactants=%s, products=%s, expression=\'%s\', local_params=%s, rxn_id=\'%s\');' event_template = 'model.addEvent(trigger=\'%s\', assignments=%s, persistent=%s, initial_value=%s, priority=%s, delay=%s, event_id=\'%s\');' event_defaults = [True, False, '0', 0] assignrule_template = 'model.addAssignmentRule(var=\'%s\', math=\'%s\');' raterule_template = 'model.addRateRule(var=\'%s\', math=\'%s\');' initassign_template = 'model.addInitialAssignment(symbol=\'%s\', math=\'%s\')' init_template = 'import simplesbml\nmodel = simplesbml.sbmlModel(time_units=\'%s\', extent_units=\'%s\', sub_units=\'%s\', level=%s, version=%s);' init_defaults = ['second', 'mole', 'mole', 3, 1] command_list = [] if doc.getLevel() == 1: print "Warning: SimpleSBML does not support Level 1 SBML models. Before \ running this code, set the model to level 2 or 3." props = libsbml.ConversionProperties() props.addOption('flatten comp', True) result = doc.convert(props) if (result != libsbml.LIBSBML_OPERATION_SUCCESS): raise SystemExit('Conversion failed: (' + str(result) + ')') mod = doc.getModel() comps = mod.getListOfCompartments() species = mod.getListOfSpecies() params = mod.getListOfParameters() rxns = mod.getListOfReactions() events = mod.getListOfEvents() rules = mod.getListOfRules() inits = [] if doc.getLevel() == 3 or (doc.getLevel() == 2 and doc.getVersion() > 1): inits = mod.getListOfInitialAssignments() timeUnits = 'second' substanceUnits = 'mole' extentUnits = 'mole' if doc.getLevel() == 3: timeUnits = mod.getTimeUnits() extentUnits = mod.getExtentUnits() substanceUnits = mod.getSubstanceUnits() level = mod.getLevel() version = mod.getVersion() init_list = [timeUnits, extentUnits, substanceUnits, level, version] for i in range(0, 5): if init_list[i] == init_defaults[i]: init_list[i] = 'del' command_list.append(init_template % \ (init_list[0], init_list[1], init_list[2], init_list[3], init_list[4])) for comp in comps: if comp.getId() != 'c1': if comp.getId()[0] == 'c' and comp.getId()[1:len(comp.getId() )].isdigit(): if comp.getSize() == 1e-15: command_list.append(comp_template % ('del', 'del')) else: command_list.append(comp_template % (comp.getSize(), 'del')) else: if comp.getSize() == 1e-15: command_list.append(comp_template % ('del', comp.getId())) else: command_list.append(comp_template % (comp.getSize(), comp.getId())) for s in species: conc = s.getInitialConcentration() amt = s.getInitialAmount() sid = s.getId() if s.getCompartment() == 'c1': comp = 'del' else: comp = s.getCompartment() bc = s.getBoundaryCondition() if bc: sid = "$" + sid if isnan(conc) or amt > conc: command_list.append(species_template % (sid, str(amt), comp)) else: command_list.append(species_template % ("[" + sid + "]", str(conc), comp)) for p in params: val = p.getValue() pid = p.getId() if p.getUnits() == 'per_second': units = 'del' else: units = p.getUnits() isDelay = pid.find('Delay') if isDelay == -1: command_list.append(param_template % (pid, str(val), str(units))) for v in rxns: vid = v.getId() if vid[0] == 'v' and vid[1:len(vid)].isdigit(): vid = 'del' reactants = [] for r in v.getListOfReactants(): reactants.append( (str(r.getStoichiometry()) + ' ' + r.getSpecies()).replace( '1.0 ', '')) products = [] for p in v.getListOfProducts(): products.append( (str(p.getStoichiometry()) + ' ' + p.getSpecies()).replace( '1.0 ', '')) expr = libsbml.formulaToString(v.getKineticLaw().getMath()) local_params = {} local_ids = [] local_values = [] for k in v.getKineticLaw().getListOfParameters(): local_ids.append(k.getId()) local_values.append(k.getValue()) local_params = dict(zip(local_ids, local_values)) if len(local_params) == 0: local_params = 'del' command_list.append(rxn_template % (str(reactants), str(products), \ expr, str(local_params), vid)) for e in events: persistent = True initialValue = False priority = '0' eid = e.getId() if len(eid) == 0 or (eid[0] == 'e' and eid[1:len(eid)].isdigit()): eid = 'del' if doc.getLevel() == 3: persistent = e.getTrigger().getPersistent() initialValue = e.getTrigger().getInitialValue() priority = e.getPriority() if type(priority) == libsbml.Priority: priority = libsbml.formulaToL3String(priority.getMath()) else: priority = '0' tri = libsbml.formulaToL3String(e.getTrigger().getMath()) did = e.getDelay() if type(did) == libsbml.Delay: delay = libsbml.formulaToL3String(did.getMath()) else: delay = '0' assigns = e.getListOfEventAssignments() var = [] values = [] for assign in assigns: var.append(assign.getVariable()) values.append(libsbml.formulaToL3String(assign.getMath())) assigns = dict(zip(var, values)) event_list = [persistent, initialValue, priority, delay] for i in range(0, 4): if event_list[i] == event_defaults[i]: event_list[i] = 'del' command_list.append(event_template % (tri, str(assigns), \ event_list[0], event_list[1], event_list[2], event_list[3], eid)) for r in rules: sym = r.getVariable() math = libsbml.formulaToL3String(r.getMath()) if r.getTypeCode() == libsbml.SBML_ASSIGNMENT_RULE: command_list.append(assignrule_template % (sym, math)) elif r.getTypeCode() == libsbml.SBML_RATE_RULE: command_list.append(raterule_template % (sym, math)) else: next for i in inits: sym = i.getSymbol() math = libsbml.formulaToL3String(i.getMath()) command_list.append(initassign_template % (sym, math)) commands = '\n'.join(command_list) commands = sub('\w+=\'?del\'?(?=[,)])', '', commands) commands = sub('\((, )+', '(', commands) commands = sub('(, )+\)', ')', commands) commands = sub('(, )+', ', ', commands) return commands
def loadModelFile(self): # Get SBMLModelPath from user input self.SBMLModelPath = str(QtGui.QFileDialog.getOpenFileName(filter = '*.xml')) # Read in SBML model file into SBML Document variable SBMLDoc = libsbml.readSBMLFromFile( self.SBMLModelPath ) # Check if any major errors in reading SBML model # e.g. Filepath does not exist if SBMLDoc.getNumErrors() > 0: self.ErrorLog.append('ERROR: File reading errors.\n') self.ErrorLog.append(SBMLDoc.getErrorLog().toString()+'\n') # Make all parameters of the model global parameters. # Enables all parameters to be vectorized. Properties = libsbml.ConversionProperties() Properties.addOption('promoteLocalParameters', True) if SBMLDoc.convert(Properties) != libsbml.LIBSBML_OPERATION_SUCCESS: self.ErrorLog.append('ERROR: Unable to convert parameters to global.\n') self.ErrorLog.append(SBMLDoc.getErrorLog().toString()+'\n') # Write out all reaction-specific function definitions. # Enables all variables in reactions to be swapped with vectorized # versions. Properties = libsbml.ConversionProperties() Properties.addOption('expandFunctionDefinitions', True) if SBMLDoc.convert(Properties) != libsbml.LIBSBML_OPERATION_SUCCESS: self.ErrorLog.append('ERROR: Unable to expand internal function usage.\n') self.ErrorLog.append(SBMLDoc.getErrorLog().toString()+'\n') # Write out all state variable and parameter initializations. # Enables this data to be placed into required SciPyModel object # places. Properties = libsbml.ConversionProperties() Properties.addOption('expandInitialAssignments', True) if SBMLDoc.convert(Properties) != libsbml.LIBSBML_OPERATION_SUCCESS: self.ErrorLog.append('ERROR: Unable to expand initial assignments.\n') self.ErrorLog.append(SBMLDoc.getErrorLog().toString()+'\n') # Extract SBML Model object from SBML Document object. self.SBMLModel = SBMLDoc.getModel() # Extract MetaData data from SBML model object self.lineEditModelName.setText(str(self.SBMLModel.getName())) self.lineEditVolumeUnits.setText(str(self.SBMLModel.getVolumeUnits())) self.lineEditQuantityUnits.setText(str(self.SBMLModel.getSubstanceUnits())) self.lineEditTimeUnits.setText(str(self.SBMLModel.getTimeUnits())) # Extract Compartment data from SBML model self.tableCompartments.setRowCount(self.SBMLModel.getNumCompartments()) for i in range(self.tableCompartments.rowCount()): CurCompartment = self.SBMLModel.getCompartment(i) self.tableCompartments.setItem(i, 0, QtGui.QTableWidgetItem('c['+str(i)+']')) if not str(CurCompartment.getName()): self.tableCompartments.setItem(i, 1, QtGui.QTableWidgetItem('default'+str(i))) self.tableCompartments.item(i, 1).setBackground(QtGui.QColor(255,255,150)) else: self.tableCompartments.setItem(i, 1, QtGui.QTableWidgetItem(str(CurCompartment.getName()))) self.tableCompartments.setItem(i, 2, QtGui.QTableWidgetItem(str(CurCompartment.volume))) self.tableCompartments.setItem(i, 3, QtGui.QTableWidgetItem(str(CurCompartment.volume))) # Add compartment to comboBox used by Species and Reactions self.comboBoxCompartments.addItem(str(CurCompartment.getName())) self.tableCompartments.resizeColumnsToContents() # Extract Species data from SBML Model self.tableSpecies.setRowCount(self.SBMLModel.getNumSpecies()) for i in range(self.tableSpecies.rowCount()): CurSpecies = self.SBMLModel.getSpecies(i) self.tableSpecies.setItem(i, 0, QtGui.QTableWidgetItem('s['+str(i)+']')) self.tableSpecies.setItem(i, 1, QtGui.QTableWidgetItem(str(CurSpecies.getName()))) self.tableSpecies.setItem(i, 2, QtGui.QTableWidgetItem(str(CurSpecies.initial_amount))) self.tableSpecies.setItem(i, 3, QtGui.QTableWidgetItem(str(CurSpecies.initial_amount))) self.tableSpecies.setItem(i, 4, QtGui.QTableWidgetItem(str(CurSpecies.compartment))) FooBox = QtGui.QCheckBox(); FooBox.setChecked(CurSpecies.boundary_condition) self.tableSpecies.setCellWidget(i, 5, FooBox) self.tableSpecies.setItem(i, 6, QtGui.QTableWidgetItem(str(CurSpecies.getMetaId()))) self.tableSpecies.resizeColumnsToContents() # Create a vector of Species names for later use ListOfSpecies = self.getTableData(self.tableSpecies, 1, 0, 1, self.SBMLModel.getNumSpecies()).flatten() # Extract Parameter data from SBML Model # -- Quantity, Names, Value, VectorIndex self.tableParameters.setRowCount(self.SBMLModel.getNumParameters()) for i in range(self.tableParameters.rowCount()): CurParameter = self.SBMLModel.getParameter(i) # Create vector index for parameter self.tableParameters.setItem(i, 0, QtGui.QTableWidgetItem('p['+str(i)+']')) # Port parameter name data from SBML model if not str(CurParameter.getName()): self.tableParameters.setItem(i, 1, QtGui.QTableWidgetItem('default_prm'+str(i))) else: self.tableParameters.setItem(i, 1, QtGui.QTableWidgetItem(str(CurParameter.getName()))) # Get parameter value data from SBML model if not str(CurParameter.getValue()): self.tableParameters.setItem(i, 2, QtGui.QTableWidgetItem('nan')) else: self.tableParameters.setItem(i, 2, QtGui.QTableWidgetItem(str(CurParameter.getValue()))) # Initialize parameter variable condtional self.tableParameters.setCellWidget(i, 3, QtGui.QCheckBox()) # Get parameter metaid data from SBML model if not str(CurParameter.getMetaId()): self.tableParameters.setItem(i, 4, QtGui.QTableWidgetItem('default_prmid'+str(i))) else: self.tableParameters.setItem(i, 4, QtGui.QTableWidgetItem(str(CurParameter.getMetaId()))) self.tableParameters.resizeColumnsToContents() # Extract Reaction data from SBML Model self.tableReactions.setRowCount(self.SBMLModel.getNumReactions()) # Construct Stoichiometric Matrix from SBML Model # Expand Stoichiometric table to correct dimensions self.tableStoichMatrix.setRowCount(self.SBMLModel.getNumSpecies()) self.tableStoichMatrix.setColumnCount(self.SBMLModel.getNumReactions()) # Place Species names on vertical header for identification self.tableStoichMatrix.setVerticalHeaderLabels(ListOfSpecies) # Create empty numpy array of correct dimension for later use StoichMatrix = numpy.empty([self.SBMLModel.getNumSpecies(), self.SBMLModel.getNumReactions()]) for i in range(self.tableReactions.rowCount()): # Get current reaction from SBML model CurReaction = self.SBMLModel.getReaction(i) # Create reaction vector index self.tableReactions.setItem(i, 0, QtGui.QTableWidgetItem(str(i))) # Port name data from SBML model if not str(CurReaction.getName()): self.tableReactions.setItem(i, 1, QtGui.QTableWidgetItem('default_rxn'+str(i))) self.tableReactions.item(i, 1).setBackground(QtGui.QColor(255,255,150)) else: self.tableReactions.setItem(i, 1, QtGui.QTableWidgetItem(str(CurReaction.getName()))) # Get current reaction differential equation Formula = str(CurReaction.getKineticLaw().getFormula()) # Replace compartment names with vector index in equation for j in reversed(range(self.tableCompartments.rowCount())): Formula = Formula.replace( str(self.tableCompartments.item(j,1).data(0).toString()), str(self.tableCompartments.item(j,0).data(0).toString()) ) # Replace parameter names with vector index in equation for j in reversed(range(self.tableParameters.rowCount())): Formula = Formula.replace( str(self.tableParameters.item(j,1).data(0).toString()), str(self.tableParameters.item(j,0).data(0).toString()) ) Formula = Formula.replace( str(self.tableParameters.item(j,4).data(0).toString()), str(self.tableParameters.item(j,0).data(0).toString()) ) # Replace species names with vector index in equation for j in reversed(range(self.tableSpecies.rowCount())): Formula = Formula.replace( str(self.tableSpecies.item(j,1).data(0).toString()), str(self.tableSpecies.item(j,0).data(0).toString()) ) Formula = Formula.replace( str(self.tableSpecies.item(j,6).data(0).toString()), str(self.tableSpecies.item(j,0).data(0).toString()) ) # Place differential equation into tableReactions self.tableReactions.setItem(i, 2, QtGui.QTableWidgetItem(Formula)) # Assemeble stoichiometric matrix into a numpy array. for r in CurReaction.getListOfReactants(): StoichMatrix[numpy.where(ListOfSpecies == r.getSpecies()), i] -= r.getStoichiometry() for p in CurReaction.getListOfProducts(): StoichMatrix[numpy.where(ListOfSpecies == p.getSpecies()), i] += p.getStoichiometry() # Assemble list of reaction modifiers for clarity ListOfModifiers = [] for m in CurReaction.getListOfModifiers(): ListOfModifiers.append(str(m.getSpecies())) # Clean string for clarity and display in table ListOfModifiers = str(ListOfModifiers).replace('[', '').replace(']', '').replace('\'', '') self.tableReactions.setItem(i, 3, QtGui.QTableWidgetItem(ListOfModifiers)) # Print compartment where reaction occurs self.tableReactions.setItem(i, 4, QtGui.QTableWidgetItem(CurReaction.getCompartment())) for i in range(self.tableStoichMatrix.rowCount()): for j in range(self.tableStoichMatrix.columnCount()): self.tableStoichMatrix.setItem(i,j,QtGui.QTableWidgetItem(str(StoichMatrix[i,j]))) self.tableStoichMatrix.setHorizontalHeaderLabels( self.getTableData(self.tableReactions, 1, 0, 1, self.SBMLModel.getNumReactions()).flatten()) # Update error log for user feedback on issues self.modelErrorLog.setPlainText(self.ErrorLog)
def importSBMLFile(SciPyModel): ''' Reads an SBML model file and unpacks the SBML model into the provided SciPyModel object structure. User may specify SBML model file path within SciPyModel manually. To-Do ----- 1. Difficulty importing Copasi exported SBML models due to difference in name/meta_id fields. Specific issue with SBML models imported/exported through Copasi. 2. Write code to inform user of required fields to be filled out. Should be in the form of: SciPyModel.errorLog() Which prints a list of potential issues in the model for the user to update. 3. Implement conditional to reset SciPyModel to initial form if user passes in filled model. Parameters ---------- SciPyModel : object instance Can be called on any SciPyModel object. Returns ------- SciPyModel : object instance Initial SciPyModel object with relevant edits in structure as dictated by the SBML model file. See Also -------- createSciPyModel Notes ----- If no file path is provided or if the provided file encounters error, then the function returns an unmodified SciPyModel object. ''' # Import required modules import libsbml, numpy, Tkinter, tkFileDialog # Conditional to check if FilePath was specified if SciPyModel.MetaData.FilePath == None: Tkinter.Tk().withdraw() SciPyModel.MetaData.FilePath = tkFileDialog.askopenfilename() else: pass # Read in SBML model file into SBML Document variable SBMLDoc = libsbml.readSBMLFromFile(SciPyModel.MetaData.FilePath) # Check if any major errors in reading SBML model # e.g. Filepath does not exist if SBMLDoc.getNumErrors() > 0: print('ERROR: File reading errors.') print(SBMLDoc.getErrorLog().toString()) # Make all parameters of the model global parameters. # Enables all parameters to be vectorized. Properties = libsbml.ConversionProperties() Properties.addOption('promoteLocalParameters', True) if SBMLDoc.convert(Properties) != libsbml.LIBSBML_OPERATION_SUCCESS: print('ERROR: Unable to convert parameters to global.') print(SBMLDoc.getErrorLog().toString()) # Write out all reaction-specific function definitions. # Enables all variables in reactions to be swapped with vectorized # versions. Properties = libsbml.ConversionProperties() Properties.addOption('expandFunctionDefinitions', True) if SBMLDoc.convert(Properties) != libsbml.LIBSBML_OPERATION_SUCCESS: print('ERROR: Unable to expand internal function usage.') print(SBMLDoc.getErrorLog().toString()) # Write out all state variable and parameter initializations. # Enables this data to be placed into required SciPyModel object # places. Properties = libsbml.ConversionProperties() Properties.addOption('expandInitialAssignments', True) if SBMLDoc.convert(Properties) != libsbml.LIBSBML_OPERATION_SUCCESS: print('ERROR: Unable to expand initial assignments.') print(SBMLDoc.getErrorLog().toString()) # Extract SBML Model object from SBML Document object. SBMLModel = SBMLDoc.getModel() # Extract MetaData # -- Name, VolumeUnits, SubstanceUnits, TimeUnits SciPyModel.MetaData.Name = SBMLModel.getName() SciPyModel.MetaData.VolumeUnits = SBMLModel.getVolumeUnits() SciPyModel.MetaData.SubstanceUnits = SBMLModel.getSubstanceUnits() SciPyModel.MetaData.TimeUnits = SBMLModel.getTimeUnits() # Extract Compartment Data # -- Quantity, Names, VectorIndex SciPyModel.Compartments.Quantity = SBMLModel.getNumCompartments() for i in range(SBMLModel.getNumCompartments()): current_compartment = SBMLModel.getCompartment(i) SciPyModel.Compartments.Names.append(current_compartment.name) SciPyModel.Compartments.VectorIndex.append(i) SciPyModel.Compartments.MetaID.append(current_compartment.meta_id) # Extract Species Data # -- Quanity, Names, Value, VectorIndex, BoundaryValue SciPyModel.Species.Quantity = SBMLModel.getNumSpecies() for i in range(SBMLModel.getNumSpecies()): current_species = SBMLModel.getSpecies(i) SciPyModel.Species.Names.append(current_species.name) SciPyModel.Species.Value.append(current_species.initial_amount) SciPyModel.Species.VectorIndex.append(i) SciPyModel.Species.BoundaryValue.append( current_species.boundary_condition) SciPyModel.Species.MetaID.append(current_species.meta_id) # Extract Parameter Data # -- Quantity, Names, Value, VectorIndex, MetaID, KineticFlag SciPyModel.Parameters.Quantity = SBMLModel.getNumParameters() for i in range(SBMLModel.getNumParameters()): current_parameter = SBMLModel.getParameter(i) SciPyModel.Parameters.Names.append(current_parameter.name) SciPyModel.Parameters.Value.append(current_parameter.value) SciPyModel.Parameters.VectorIndex.append(i) SciPyModel.Parameters.MetaID.append(current_parameter.meta_id) [ SciPyModel.Parameters.KineticFlag.append(False) for i in range(SciPyModel.Parameters.Quantity) ] # Extract Reaction Data # -- Names, Formulas, Stoichiometry SciPyModel.Reactions.Stoichiometry = numpy.zeros( [SBMLModel.getNumSpecies(), SBMLModel.getNumReactions()]) SciPyModel.Reactions.Quantity = SBMLModel.getNumReactions() for i in range(SBMLModel.getNumReactions()): current_reaction = SBMLModel.getReaction(i) SciPyModel.Reactions.Names.append(current_reaction.name) SciPyModel.Reactions.Formulas.append( current_reaction.getKineticLaw().getFormula()) # Try-Except in order to see if Names or MetaID are used in the functions try: for r in current_reaction.getListOfReactants(): SciPyModel.Reactions.Stoichiometry[ SciPyModel.Species.Names.index(r.getSpecies()), i] -= r.getStoichiometry() for p in current_reaction.getListOfProducts(): SciPyModel.Reactions.Stoichiometry[ SciPyModel.Species.Names.index(p.getSpecies()), i] += p.getStoichiometry() except ValueError: for r in current_reaction.getListOfReactants(): SciPyModel.Reactions.Stoichiometry[ SciPyModel.Species.MetaID.index(r.getSpecies()), i] -= r.getStoichiometry() for p in current_reaction.getListOfProducts(): SciPyModel.Reactions.Stoichiometry[ SciPyModel.Species.MetaID.index(p.getSpecies()), i] += p.getStoichiometry() else: print 'ERROR: Unable to create Stoichiometric Matrix. Check species name/metaid.' # Remove Stoichiometry of Boundary State Variables for s in range(SciPyModel.Species.Quantity): if SciPyModel.Species.BoundaryValue[s]: SciPyModel.Reactions.Stoichiometry[s, :] = numpy.zeros( (1, SciPyModel.Reactions.Quantity)) # Vectorize Functions within SciPyModel object. for rxn_ix in range(SciPyModel.Reactions.Quantity): # Get information about the current reaction Formula = SciPyModel.Reactions.Formulas[rxn_ix] # Removes usage of compartments from reaction equations. for j in reversed(range(SciPyModel.Compartments.Quantity)): if SciPyModel.Compartments.Names[j] != '': # If name isn't empty Formula = Formula.replace( SciPyModel.Compartments.Names[j] + ' * ', '') Formula = Formula.replace( ' * ' + SciPyModel.Compartments.Names[j], '') Formula = Formula.replace( ' / ' + SciPyModel.Compartments.Names[j], '') # Replace parameter names with index of vectorized parameter array. # Iterates through parameter names sorted by length of name. for key in sorted(SciPyModel.Parameters.Names, key=len, reverse=True): if key != '': Formula = Formula.replace( key, 'p[' + str(SciPyModel.Parameters.Names.index(key)) + ']') for key in sorted(SciPyModel.Parameters.MetaID, key=len, reverse=True): if key != '': Formula = Formula.replace( key, 'p[' + str(SciPyModel.Parameters.MetaID.index(key)) + ']') # Replace species names with index of species parameter array. for key in sorted(SciPyModel.Species.Names, key=len, reverse=True): if key != '': Formula = Formula.replace( key, 'y[' + str(SciPyModel.Species.Names.index(key)) + ']') for key in sorted(SciPyModel.Species.MetaID, key=len, reverse=True): if key != '': Formula = Formula.replace( key, 'y[' + str(SciPyModel.Species.MetaID.index(key)) + ']') # Reset formula declaration in SciPyModel class SciPyModel.Reactions.Formulas[rxn_ix] = Formula # Grab indecies of kinetic rate constant parameters ReactionIndex = [] for rxn_ix in range(SciPyModel.Reactions.Quantity): ReactionIndex.append( int(SciPyModel.Reactions.Formulas[rxn_ix].split(']')[0].split('[') [-1])) # Order the parameters and track the internal index ordering SortedIndex = [ i[0] for i in sorted(enumerate(ReactionIndex), key=lambda x: x[1]) ] # Apply the index ordering to order the formula vector SciPyModel.Reactions.Formulas = [ SciPyModel.Reactions.Formulas[i] for i in SortedIndex ] # Apply the index ordering to order the stoichiometry matrix SciPyModel.Reactions.Stoichiometry = numpy.vstack([ SciPyModel.Reactions.Stoichiometry[:, i] for i in SortedIndex ]).transpose() return SciPyModel
from __future__ import absolute_import, print_function import libsbml import time model_paths = ["rbc_top.xml"] for p in model_paths: t_start = time.time() print("\n*** {} ***".format(p)) doc = libsbml.readSBMLFromFile(p) t_read = time.time() print('reading: {:5.3} [s]'.format(t_read - t_start)) # converter options props = libsbml.ConversionProperties() props.addOption("flatten comp", True) # Invokes CompFlatteningConverter props.addOption("leave_ports", True) # Indicates whether to leave ports # convert result = doc.convert(props) if result != libsbml.LIBSBML_OPERATION_SUCCESS: doc.printErrors() print("model could not be flattended due to errors.") t_flat = time.time() print('flattening: {:5.3} [s]'.format(t_flat - t_start))
def convert_function_definitions(doc): props = libsbml.ConversionProperties() props.addOption("expandFunctionDefinitions", True) return doc.convert(props)
def sbml_to_ode(filename): # # read the SBML from file # import libsbml as lb import sys raise NotImplementedError doc = lb.readSBMLFromFile(filename) if doc.getNumErrors(lb.LIBSBML_SEV_FATAL): print('Encountered serious errors while reading file') print(doc.getErrorLog().toString()) sys.exit(1) # clear errors doc.getErrorLog().clearLog() # # perform conversions # props = lb.ConversionProperties() props.addOption("promoteLocalParameters", True) if doc.convert(props) != lb.LIBSBML_OPERATION_SUCCESS: print('The document could not be converted') print(doc.getErrorLog().toString()) props = lb.ConversionProperties() props.addOption("expandInitialAssignments", True) if doc.convert(props) != lb.LIBSBML_OPERATION_SUCCESS: print('The document could not be converted') print(doc.getErrorLog().toString()) props = lb.ConversionProperties() props.addOption("expandFunctionDefinitions", True) if doc.convert(props) != lb.LIBSBML_OPERATION_SUCCESS: print('The document could not be converted') print(doc.getErrorLog().toString()) # # figure out which species are variable # mod = doc.getModel() variables = {} for i in range(mod.getNumSpecies()): species = mod.getSpecies(i) if species.getBoundaryCondition() == True or species.getId() in variables.keys(): continue variables[species.getId()] = [] # # start generating the code by appending to bytearray # generated_code = bytearray('') generated_code.extend('from numpy import *\n') generated_code.extend('from matplotlib.pylab import *\n') generated_code.extend('from matplotlib.pyplot import *\n') generated_code.extend('from scipy.integrate import odeint \n') generated_code.extend('\n') generated_code.extend('def simulateModel(t0, tend, numPoints):\n') # write out compartment values generated_code.extend(' \n') generated_code.extend(' #compartments\n') for i in range(mod.getNumCompartments()): element = mod.getCompartment(i) generated_code.extend(' {0} = {1}\n'.format(element.getId(), element.getSize())) # write out parameter values generated_code.extend(' \n') generated_code.extend(' #global parameters\n') for i in range(mod.getNumParameters()): element = mod.getParameter(i) generated_code.extend(' {0} = {1}\n'.format(element.getId(), element.getValue())) # write out boundary species generated_code.extend(' \n') generated_code.extend(' #boundary species\n') for i in range(mod.getNumSpecies()): element = mod.getSpecies(i) if element.getBoundaryCondition() == False: continue if element.isSetInitialConcentration(): generated_code.extend(' {0} = {1}\n'.format(element.getId(), element.getInitialConcentration())) else: generated_code.extend(' {0} = {1}\n'.format(element.getId(), element.getInitialAmount())) # write derive function generated_code.extend(' \n') generated_code.extend(' def ode_fun(__Y__, t):\n') for i in range(len(variables.keys())): generated_code.extend(' {0} = __Y__[{1}]\n'.format(variables.keys()[i], i)) generated_code.extend('\n') for i in range(mod.getNumReactions()): reaction = mod.getReaction(i) kinetics = reaction.getKineticLaw() generated_code.extend(' {0} = {1}\n'.format(reaction.getId(), kinetics.getFormula())) for j in range(reaction.getNumReactants()): ref = reaction.getReactant(j) species = mod.getSpecies(ref.getSpecies()) if species.getBoundaryCondition() == True: continue if ref.getStoichiometry() == 1.0: variables[species.getId()].append('-{0}'.format(reaction.getId())) else: variables[species.getId()].append('-({0})*{1}'.format(ref.getStoichiometry(), reaction.getId())) for j in range(reaction.getNumProducts()): ref = reaction.getProduct(j) species = mod.getSpecies(ref.getSpecies()) if species.getBoundaryCondition() == True: continue if ref.getStoichiometry() == 1.0: variables[species.getId()].append('{0}'.format(reaction.getId())) else: variables[species.getId()].append('({0})*{1}'.format(ref.getStoichiometry(), reaction.getId())) generated_code.extend('\n') generated_code.extend(' return array([') for i in range(len(variables.keys())): for eqn in variables[variables.keys()[i]]: generated_code.extend(' + ({0})'.format(eqn)) if i + 1 < len(variables.keys()): generated_code.extend(',\n ') generated_code.extend(' ])\n') generated_code.extend('\n') generated_code.extend(' time = linspace(t0, tend, numPoints)\n') # # write out initial concentrations # generated_code.extend(' yinit= array([') count = 0 for key in variables.keys(): # get initialValue element = mod.getElementBySId(key) if element.getTypeCode() == lb.SBML_PARAMETER: generated_code.extend('{0}'.format(element.getValue())) elif element.getTypeCode() == lb.SBML_SPECIES: if element.isSetInitialConcentration(): generated_code.extend('{0}'.format(element.getInitialConcentration())) else: generated_code.extend('{0}'.format(element.getInitialAmount())) else: generated_code.extend('{0}'.format(element.getSize())) count += 1 if count < len(variables.keys()): generated_code.extend(', ') generated_code.extend('])\n') generated_code.extend(' \n') generated_code.extend(' y = odeint(ode_fun, yinit, time)\n') generated_code.extend('\n') generated_code.extend(' return time, y\n') generated_code.extend('\n') generated_code.extend('\n') generated_code.extend('time, result = simulateModel({0}, {1}, {2})\n'.format(t0, tEnd, numPoints)) generated_code.extend('\n') # # write out plotting code # generated_code.extend('fig = figure()\n') generated_code.extend('ax = subplot(111)\n') for i in range(len(variables.keys())): generated_code.extend('plot(time,result[:,{0}], label="{1}", lw=1.5)\n'.format(i, variables.keys()[i])) generated_code.extend('box = ax.get_position()\n') generated_code.extend('ax.set_position([box.x0, box.y0, box.width * 0.7, box.height])\n') generated_code.extend('xlabel("time")\n') generated_code.extend('ylabel("concentration")\n') generated_code.extend('legend(loc="center left", bbox_to_anchor=(1, 0.5))\n') generated_code.extend('show()\n') # convert generated code to string result = str(generated_code); return simulateModel
def flattenSBMLDocument(doc, leave_ports=True, output_path=None, suffix='_flat'): """ Flatten the given SBMLDocument. Validation should be performed before the flattening and is not part of the flattening routine. :param doc: SBMLDocument to flatten. :type doc: SBMLDocument :return: :rtype: SBMLDocument """ Nerrors = doc.getNumErrors() if Nerrors > 0: if doc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. logging.error("SBML error in doc: libsbml.XMLFileUnreadable") elif doc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. logging.error("SBML error in doc: libsbml.XMLFileOperationError") else: # Handle other error cases here. logging.error("SBML errors in doc, see SBMLDocument error log.") # converter options props = libsbml.ConversionProperties() props.addOption("flatten comp", True) # Invokes CompFlatteningConverter props.addOption("leave_ports", leave_ports) # Indicates whether to leave ports # flatten current = time.clock() result = doc.convert(props) flattened_status = (result==libsbml.LIBSBML_OPERATION_SUCCESS) lines = [ '', '-' * 120, str(doc), "{:<25}: {}".format("flattened", str(flattened_status).upper()), "{:<25}: {:.3f}".format("flatten time (ms)", time.clock() - current), '-' * 120, ] info = "\n".join(lines) if flattened_status: logging.info(info) else: logging.error(info) raise ValueError("SBML could not be flattend due to errors in the SBMLDocument.") if suffix is not None: model = doc.getModel() if model is not None: model.setId(model.getId() + suffix) if model.isSetName(): model.setName(model.getName() + suffix) if output_path is not None: # Write the results to the output file. libsbml.writeSBMLToFile(doc, output_path) logging.info("Flattened model written to {}".format(output_path)) return doc
def flatten_sbml_doc(doc: libsbml.SBMLDocument, output_path: Path = None, leave_ports: bool = True) -> libsbml.SBMLDocument: """Flatten SBMLDocument. Validation should be performed before the flattening and is not part of the flattening routine. If an output path is provided the file is written to the output path. :param doc: SBMLDocument to flatten. :param output_path: Path to write flattended SBMLDocument to :param leave_ports: flag to leave ports :return: SBMLDocument """ error_count = doc.getNumErrors() if error_count > 0: if doc.getError(0).getErrorId() == libsbml.XMLFileUnreadable: # Handle case of unreadable file here. logger.error("SBML error in doc: libsbml.XMLFileUnreadable") elif doc.getError(0).getErrorId() == libsbml.XMLFileOperationError: # Handle case of other file error here. logger.error("SBML error in doc: libsbml.XMLFileOperationError") else: # Handle other error cases here. logger.error("SBML errors in doc, see SBMLDocument error log.") # converter options libsbml.CompFlatteningConverter props = libsbml.ConversionProperties() props.addOption("flatten comp", True) # Invokes CompFlatteningConverter props.addOption("leave_ports", leave_ports) # Indicates whether to leave ports props.addOption("abortIfUnflattenable", "none") # flatten current = time.perf_counter() result = doc.convert(props) flattened_status = result == libsbml.LIBSBML_OPERATION_SUCCESS lines = [ "", "-" * 120, str(doc), "{:<25}: {}".format("flattened", str(flattened_status).upper()), "{:<25}: {:.3f}".format("flatten time (ms)", time.perf_counter() - current), "-" * 120, ] info = bcolors.BOLD + "\n".join(lines) + bcolors.ENDC if flattened_status: logger.info(bcolors.OKGREEN + info + bcolors.ENDC) else: logger.error(bcolors.FAIL + info + bcolors.ENDC) raise ValueError( "SBML could not be flattend due to errors in the SBMLDocument.") if output_path is not None: write_sbml(doc, filepath=output_path) logger.info(f"Flattened model created: '{output_path}'") return doc
def convert(self, level=(3, 2)): """ Convert the PySB model to a libSBML document Requires the libsbml python package Parameters ---------- level: (int, int) The SBML level and version to use. The default is SBML level 3, version 2. Conversion to other levels/versions may not be possible or may lose fidelity. Returns ------- libsbml.SBMLDocument A libSBML document converted form the PySB model """ doc = libsbml.SBMLDocument(3, 2) smodel = doc.createModel() _check(smodel) _check(smodel.setName(self.model.name)) pysb.bng.generate_equations(self.model) # Docstring if self.docstring: notes_str = """ <notes> <body xmlns="http://www.w3.org/1999/xhtml"> <p>%s</p> </body> </notes>""" % self.docstring.replace("\n", "<br />\n" + " " * 20) _check(smodel.setNotes(notes_str)) # Compartments if self.model.compartments: for cpt in self.model.compartments: c = smodel.createCompartment() _check(c) _check(c.setId(cpt.name)) _check(c.setSpatialDimensions(cpt.dimension)) _check(c.setSize(cpt.size.value)) _check(c.setConstant(True)) else: c = smodel.createCompartment() _check(c) _check(c.setId('default')) _check(c.setSpatialDimensions(3)) _check(c.setSize(1)) _check(c.setConstant(True)) # Expressions for i, expr in enumerate(self.model.expressions): # create an observable "parameter" e = smodel.createParameter() _check(e) _check(e.setId(expr.name)) _check(e.setName(expr.name)) _check(e.setConstant(False)) # create an assignment rule which assigns the expression to the parameter expr_rule = smodel.createAssignmentRule() _check(expr_rule) _check(expr_rule.setVariable(e.getId())) expr_mathml = self._sympy_to_sbmlast( expr.expand_expr(expand_observables=True)) _check(expr_rule.setMath(expr_mathml)) # Initial values/assignments fixed_species_idx = set() initial_species_idx = set() for ic in self.model.initials: sp_idx = self.model.get_species_index(ic.pattern) ia = smodel.createInitialAssignment() _check(ia) _check(ia.setSymbol('__s{}'.format(sp_idx))) init_mathml = self._sympy_to_sbmlast(Symbol(ic.value.name)) _check(ia.setMath(init_mathml)) initial_species_idx.add(sp_idx) if ic.fixed: fixed_species_idx.add(sp_idx) # Species for i, s in enumerate(self.model.species): sp = smodel.createSpecies() _check(sp) _check(sp.setId('__s{}'.format(i))) if self.model.compartments: # Try to determine compartment, which must be unique for the species mon_cpt = set(mp.compartment for mp in s.monomer_patterns if mp.compartment is not None) if len(mon_cpt) == 0 and s.compartment: compartment_name = s.compartment_name elif len(mon_cpt) == 1: mon_cpt = mon_cpt.pop() if s.compartment is not None and mon_cpt != s.compartment: raise ValueError( 'Species {} has different monomer and species compartments, ' 'which is not supported in SBML'.format(s)) compartment_name = mon_cpt.name else: raise ValueError( 'Species {} has more than one different monomer compartment, ' 'which is not supported in SBML'.format(s)) else: compartment_name = 'default' _check(sp.setCompartment(compartment_name)) _check(sp.setName(str(s).replace('% ', '._br_'))) _check(sp.setBoundaryCondition(i in fixed_species_idx)) _check(sp.setConstant(False)) _check(sp.setHasOnlySubstanceUnits(True)) if i not in initial_species_idx: _check(sp.setInitialAmount(0.0)) # Parameters for i, param in enumerate(self.model.parameters): p = smodel.createParameter() _check(p) _check(p.setId(param.name)) _check(p.setName(param.name)) _check(p.setValue(param.value)) _check(p.setConstant(True)) # Reactions for i, reaction in enumerate(self.model.reactions_bidirectional): rxn = smodel.createReaction() _check(rxn) _check(rxn.setId('r{}'.format(i))) _check(rxn.setName(' + '.join(reaction['rule']))) _check(rxn.setReversible(reaction['reversible'])) for sp in reaction['reactants']: reac = rxn.createReactant() _check(reac) _check(reac.setSpecies('__s{}'.format(sp))) _check(reac.setConstant(True)) for sp in reaction['products']: prd = rxn.createProduct() _check(prd) _check(prd.setSpecies('__s{}'.format(sp))) _check(prd.setConstant(True)) for symbol in reaction['rate'].free_symbols: if isinstance(symbol, pysb.Expression): expr = symbol.expand_expr(expand_observables=True) for sym in expr.free_symbols: if not isinstance(sym, (pysb.Parameter, pysb.Expression)): # Species reference, needs to be specified as modifier modifier = rxn.createModifier() _check(modifier) _check(modifier.setSpecies(str(sym))) rate = rxn.createKineticLaw() _check(rate) rate_mathml = self._sympy_to_sbmlast(reaction['rate']) _check(rate.setMath(rate_mathml)) # Observables for i, observable in enumerate(self.model.observables): # create an observable "parameter" obs = smodel.createParameter() _check(obs) _check(obs.setId('__obs{}'.format(i))) _check(obs.setName(observable.name)) _check(obs.setConstant(False)) # create an assignment rule which assigns the observable expression to the parameter obs_rule = smodel.createAssignmentRule() _check(obs_rule) _check(obs_rule.setVariable(obs.getId())) obs_mathml = self._sympy_to_sbmlast(observable.expand_obs()) _check(obs_rule.setMath(obs_mathml)) # Apply any requested level/version conversion if level != (3, 2): prop = libsbml.ConversionProperties(libsbml.SBMLNamespaces(*level)) prop.addOption('strict', False) prop.addOption('setLevelAndVersion', True) prop.addOption('ignorePackages', True) _check(doc.convert(prop)) return doc
def read_sbml_ec_model( filename: str, number: float = float, # re.Pattern does not exist in py36 so this type hint cannot be added now f_replace=F_REPLACE, set_missing_bounds: bool = False, hardcoded_rev_reactions: bool = True, **kwargs, ) -> Model: """Create `geckopy.Model` from SBMLDocument. Parameters ---------- filename: str number: data type of stoichiometry: {float, int} In which data type should the stoichiometry be parsed. f_replace : dict of replacement functions for id replacement set_missing_bounds : flag to set missing bounds hardcoded_rev_reactions: bool if reversible reaction to account for proteins being consumed on both directions are written explicitly for in the SBML Returns ------- cobra.core.Model """ try: fsanitized = str(filename) if isinstance(filename, Path) else filename doc = _get_doc_from_filename(fsanitized) except IOError as e: raise e except Exception as original_error: raise CobraSBMLError( "Something went wrong reading the SBML model. Most likely the SBML" " model is not valid. Please check that your model is valid using " "the `cobra.io.sbml.validate_sbml_model` function or via the " "online validator at http://sbml.org/validator .\n" "\t`(model, errors) = validate_sbml_model(filename)`" "\nIf the model is valid and cannot be read please open an issue " f"at https://github.com/opencobra/cobrapy/issues: {original_error}" ) if f_replace is None: f_replace = {} # SBML model model: libsbml.Model = doc.getModel() if model is None: raise CobraSBMLError("No SBML model detected in file.") model_fbc: libsbml.FbcModelPlugin = model.getPlugin("fbc") if not model_fbc: LOGGER.warning("Model does not contain SBML fbc package information.") else: if not model_fbc.isSetStrict(): LOGGER.warning('Loading SBML model without fbc:strict="true"') # fbc-v1 (legacy) doc_fbc = doc.getPlugin("fbc") # type: libsbml.FbcSBMLDocumentPlugin fbc_version = doc_fbc.getPackageVersion() if fbc_version == 1: LOGGER.warning("Loading SBML with fbc-v1 (models should be encoded" " using fbc-v2)") conversion_properties = libsbml.ConversionProperties() conversion_properties.addOption("convert fbc v1 to fbc v2", True, "Convert FBC-v1 model to FBC-v2") result = doc.convert(conversion_properties) if result != libsbml.LIBSBML_OPERATION_SUCCESS: raise Exception("Conversion of SBML fbc v1 to fbc v2 failed") # Model model_id = model.getIdAttribute() if not libsbml.SyntaxChecker.isValidSBMLSId(model_id): LOGGER.error("'%s' is not a valid SBML 'SId'." % model_id) geckopy_model = Model(model_id, hardcoded_rev_reactions=hardcoded_rev_reactions) geckopy_model.name = model.getName() # meta information meta = { "model.id": model_id, "level": model.getLevel(), "version": model.getVersion(), "packages": [], } # History creators = [] created = None if model.isSetModelHistory(): history = model.getModelHistory() # type: libsbml.ModelHistory if history.isSetCreatedDate(): created = history.getCreatedDate() for c in history.getListCreators(): # type: libsbml.ModelCreator creators.append({ "familyName": c.getFamilyName() if c.isSetFamilyName() else None, "givenName": c.getGivenName() if c.isSetGivenName() else None, "organisation": c.getOrganisation() if c.isSetOrganisation() else None, "email": c.getEmail() if c.isSetEmail() else None, }) meta["creators"] = creators meta["created"] = created meta["notes"] = _parse_notes_dict(doc) meta["annotation"] = _parse_annotations(doc) info = "<{}> SBML L{}V{}".format(model_id, model.getLevel(), model.getVersion()) packages = {} for k in range(doc.getNumPlugins()): plugin = doc.getPlugin(k) # type:libsbml.SBasePlugin key, value = plugin.getPackageName(), plugin.getPackageVersion() packages[key] = value info += ", {}-v{}".format(key, value) if key not in ["fbc", "groups", "l3v2extendedmath"]: LOGGER.warning( "SBML package '%s' not supported by cobrapy, " "information is not parsed", key, ) meta["info"] = info meta["packages"] = packages geckopy_model._sbml = meta # notes and annotations geckopy_model.notes = _parse_notes_dict(model) geckopy_model.annotation = _parse_annotations(model) # Compartments # FIXME: update with new compartments compartments = {} for (compartment) in model.getListOfCompartments( ): # noqa: E501 type: libsbml.Compartment cid = _check_required(compartment, compartment.getIdAttribute(), "id") compartments[cid] = compartment.getName() geckopy_model.compartments = compartments # Species metabolites = [] # proteins that rely on the naming convention "prot_UNIPROT_ID" will be # catched here. Those who are annotated by groups membership will be parsed # after the groups are processed. proteins = [] boundary_metabolites = [] if model.getNumSpecies() == 0: LOGGER.warning("No metabolites in model") for specie in model.getListOfSpecies(): # type: libsbml.Species sid = _check_required(specie, specie.getIdAttribute(), "id") if f_replace and F_SPECIE in f_replace: sid = f_replace[F_SPECIE](sid) met = Metabolite(sid) met.name = specie.getName() met.notes = _parse_notes_dict(specie) met.annotation = _parse_annotations(specie) met.compartment = specie.getCompartment() initial_amount = specie.getInitialAmount() specie_fbc = specie.getPlugin("fbc") # type: libsbml.FbcSpeciesPlugin if specie_fbc: met.charge = specie_fbc.getCharge() met.formula = specie_fbc.getChemicalFormula() else: if specie.isSetCharge(): LOGGER.warning( "Use of the species charge attribute is " "discouraged, use fbc:charge " "instead: %s", specie, ) met.charge = specie.getCharge() else: if "CHARGE" in met.notes: LOGGER.warning( "Use of CHARGE in the notes element is " "discouraged, use fbc:charge " "instead: %s", specie, ) try: met.charge = int(met.notes["CHARGE"]) except ValueError: # handle nan, na, NA, ... pass if "FORMULA" in met.notes: LOGGER.warning( "Use of FORMULA in the notes element is " "discouraged, use fbc:chemicalFormula " "instead: %s", specie, ) met.formula = met.notes["FORMULA"] # Detect boundary metabolites if specie.getBoundaryCondition() is True: boundary_metabolites.append(met) if not PROT_PATTERN.match(met.id): metabolites.append(met) else: proteins.append(Protein(met, concentration=initial_amount)) geckopy_model.add_metabolites(metabolites) geckopy_model.add_proteins(proteins) # Add exchange reactions for boundary metabolites ex_reactions = [] for met in boundary_metabolites: ex_rid = "EX_{}".format(met.id) ex_reaction = Reaction(ex_rid) ex_reaction.name = ex_rid ex_reaction.annotation = {"sbo": SBO_EXCHANGE_REACTION} ex_reaction.lower_bound = config.lower_bound ex_reaction.upper_bound = config.upper_bound LOGGER.warning("Adding exchange reaction %s with default bounds " "for boundary metabolite: %s." % (ex_reaction.id, met.id)) # species is reactant ex_reaction.add_metabolites({met: -1}) ex_reactions.append(ex_reaction) geckopy_model.add_reactions(ex_reactions) # Genes if model_fbc: for (gp) in model_fbc.getListOfGeneProducts( ): # noqa: E501 type: libsbml.GeneProduct gid = _check_required(gp, gp.getIdAttribute(), "id") if f_replace and F_GENE in f_replace: gid = f_replace[F_GENE](gid) cobra_gene = Gene(gid) cobra_gene.name = gp.getName() if cobra_gene.name is None: cobra_gene.name = gid cobra_gene.annotation = _parse_annotations(gp) cobra_gene.notes = _parse_notes_dict(gp) geckopy_model.genes.append(cobra_gene) else: for (cobra_reaction) in model.getListOfReactions( ): # noqa: E501 type: libsbml.Reaction # fallback to notes information notes = _parse_notes_dict(cobra_reaction) if "GENE ASSOCIATION" in notes: gpr = notes["GENE ASSOCIATION"] elif "GENE_ASSOCIATION" in notes: gpr = notes["GENE_ASSOCIATION"] else: gpr = "" if len(gpr) > 0: gpr = gpr.replace("(", ";") gpr = gpr.replace(")", ";") gpr = gpr.replace("or", ";") gpr = gpr.replace("and", ";") # Interaction of the above replacements can lead to multiple # ;, which results in empty gids gids = [t.strip() for t in gpr.split(";")] gids = set(gids).difference({""}) # create missing genes for gid in gids: if f_replace and F_GENE in f_replace: gid = f_replace[F_GENE](gid) if gid not in geckopy_model.genes: cobra_gene = Gene(gid) cobra_gene.name = gid geckopy_model.genes.append(cobra_gene) # GPR rules def process_association(ass): """Recursively convert gpr association to a gpr string. Defined as inline functions to not pass the replacement dict around. """ if ass.isFbcOr(): return " ".join([ "(", " or ".join( process_association(c) for c in ass.getListOfAssociations()), ")", ]) elif ass.isFbcAnd(): return " ".join([ "(", " and ".join( process_association(c) for c in ass.getListOfAssociations()), ")", ]) elif ass.isGeneProductRef(): gid = ass.getGeneProduct() if f_replace and F_GENE in f_replace: return f_replace[F_GENE](gid) else: return gid # Reactions missing_bounds = False reactions = [] if model.getNumReactions() == 0: LOGGER.warning("No reactions in model") for reaction in model.getListOfReactions(): # type: libsbml.Reaction rid = _check_required(reaction, reaction.getIdAttribute(), "id") # proteins are parsed based on Species, prot exchanges are ignored if PROT_EX_PATTERN.search(rid): continue if f_replace and F_REACTION in f_replace: rid = f_replace[F_REACTION](rid) cobra_reaction = Reaction(rid) cobra_reaction.name = reaction.getName() cobra_reaction.annotation = _parse_annotations(reaction) cobra_reaction.notes = _parse_notes_dict(reaction) # set bounds p_ub, p_lb = None, None r_fbc = reaction.getPlugin("fbc") # type: libsbml.FbcReactionPlugin if r_fbc: # bounds in fbc lb_id = r_fbc.getLowerFluxBound() if lb_id: p_lb = model.getParameter(lb_id) # type: libsbml.Parameter if p_lb and p_lb.getConstant() and (p_lb.getValue() is not None): cobra_reaction.lower_bound = p_lb.getValue() else: raise CobraSBMLError("No constant bound '%s' for " "reaction: %s" % (p_lb, reaction)) ub_id = r_fbc.getUpperFluxBound() if ub_id: p_ub = model.getParameter(ub_id) # type: libsbml.Parameter if p_ub and p_ub.getConstant() and (p_ub.getValue() is not None): cobra_reaction.upper_bound = p_ub.getValue() else: raise CobraSBMLError("No constant bound '%s' for " "reaction: %s" % (p_ub, reaction)) elif reaction.isSetKineticLaw(): # some legacy models encode bounds in kinetic laws klaw = reaction.getKineticLaw() # type: libsbml.KineticLaw p_lb = klaw.getParameter( "LOWER_BOUND") # noqa: E501 type: libsbml.LocalParameter if p_lb: cobra_reaction.lower_bound = p_lb.getValue() p_ub = klaw.getParameter( "UPPER_BOUND") # noqa: E501 type: libsbml.LocalParameter if p_ub: cobra_reaction.upper_bound = p_ub.getValue() if p_ub is not None or p_lb is not None: LOGGER.warning( "Encoding LOWER_BOUND and UPPER_BOUND in " "KineticLaw is discouraged, " "use fbc:fluxBounds instead: %s", reaction, ) if p_lb is None: missing_bounds = True lower_bound = config.lower_bound cobra_reaction.lower_bound = lower_bound LOGGER.warning( "Missing lower flux bound set to '%s' for " " reaction: '%s'", lower_bound, reaction, ) if p_ub is None: missing_bounds = True upper_bound = config.upper_bound cobra_reaction.upper_bound = upper_bound LOGGER.warning( "Missing upper flux bound set to '%s' for " " reaction: '%s'", upper_bound, reaction, ) # add reaction reactions.append(cobra_reaction) # parse equation stoichiometry = defaultdict(lambda: 0) for (sref) in reaction.getListOfReactants( ): # noqa: E501 type: libsbml.SpeciesReference sid = _check_required(sref, sref.getSpecies(), "species") if f_replace and F_SPECIE in f_replace: sid = f_replace[F_SPECIE](sid) stoichiometry[sid] -= number( _check_required(sref, sref.getStoichiometry(), "stoichiometry")) for (sref) in reaction.getListOfProducts( ): # noqa: E501 type: libsbml.SpeciesReference sid = _check_required(sref, sref.getSpecies(), "species") if f_replace and F_SPECIE in f_replace: sid = f_replace[F_SPECIE](sid) stoichiometry[sid] += number( _check_required(sref, sref.getStoichiometry(), "stoichiometry")) # convert to metabolite objects object_stoichiometry = {} for met_id in stoichiometry: target_set = (geckopy_model.proteins if met_id in geckopy_model.proteins else geckopy_model.metabolites) metabolite = target_set.get_by_id(met_id) object_stoichiometry[metabolite] = stoichiometry[met_id] cobra_reaction.add_metabolites(object_stoichiometry) # GPR if r_fbc: gpr = "" gpa = (r_fbc.getGeneProductAssociation() ) # noqa: E501 type: libsbml.GeneProductAssociation if gpa is not None: association = (gpa.getAssociation() ) # noqa: E501 type: libsbml.FbcAssociation gpr = process_association(association) else: # fallback to notes information notes = cobra_reaction.notes if "GENE ASSOCIATION" in notes: gpr = notes["GENE ASSOCIATION"] elif "GENE_ASSOCIATION" in notes: gpr = notes["GENE_ASSOCIATION"] else: gpr = "" if len(gpr) > 0: LOGGER.warning( "Use of GENE ASSOCIATION or GENE_ASSOCIATION " "in the notes element is discouraged, use " "fbc:gpr instead: %s", reaction, ) if f_replace and F_GENE in f_replace: gpr = " ".join(f_replace[F_GENE](t) for t in gpr.split(" ")) # remove outside parenthesis, if any if gpr.startswith("(") and gpr.endswith(")"): try: parse_gpr(gpr[1:-1].strip()) gpr = gpr[1:-1].strip() except (SyntaxError, TypeError) as e: LOGGER.warning( f"Removing parenthesis from gpr {gpr} leads to " f"an error, so keeping parenthesis, error: {e}", ) cobra_reaction.gene_reaction_rule = gpr geckopy_model.add_reactions(reactions) # Objective obj_direction = "max" coefficients = {} if model_fbc: obj_list = (model_fbc.getListOfObjectives() ) # noqa: E501 type: libsbml.ListOfObjectives if obj_list is None: LOGGER.warning("listOfObjectives element not found") elif obj_list.size() == 0: LOGGER.warning("No objective in listOfObjectives") elif not obj_list.getActiveObjective(): LOGGER.warning("No active objective in listOfObjectives") else: obj_id = obj_list.getActiveObjective() obj = model_fbc.getObjective(obj_id) # type: libsbml.Objective obj_direction = LONG_SHORT_DIRECTION[obj.getType()] for (flux_obj) in (obj.getListOfFluxObjectives() ): # noqa: E501 type: libsbml.FluxObjective rid = flux_obj.getReaction() if f_replace and F_REACTION in f_replace: rid = f_replace[F_REACTION](rid) try: objective_reaction = geckopy_model.reactions.get_by_id(rid) except KeyError: raise CobraSBMLError("Objective reaction '%s' " "not found" % rid) try: coefficients[objective_reaction] = number( flux_obj.getCoefficient()) except ValueError as e: LOGGER.warning(str(e)) else: # some legacy models encode objective coefficients in kinetic laws for reaction in model.getListOfReactions(): # type: libsbml.Reaction if reaction.isSetKineticLaw(): klaw = reaction.getKineticLaw() # type: libsbml.KineticLaw p_oc = klaw.getParameter( "OBJECTIVE_COEFFICIENT") # type: libsbml.LocalParameter if p_oc: rid = _check_required(reaction, reaction.getIdAttribute(), "id") if f_replace and F_REACTION in f_replace: rid = f_replace[F_REACTION](rid) try: objective_reaction = geckopy_model.reactions.get_by_id( rid) except KeyError: raise CobraSBMLError( "Objective reaction '%s' " "not found", rid) try: coefficients[objective_reaction] = number( p_oc.getValue()) except ValueError as e: LOGGER.warning(str(e)) LOGGER.warning( "Encoding OBJECTIVE_COEFFICIENT in " "KineticLaw is discouraged, " "use fbc:fluxObjective " "instead: %s", reaction, ) if len(coefficients) == 0: LOGGER.error("No objective coefficients in model. Unclear what should " "be optimized") set_objective(geckopy_model, coefficients) geckopy_model.solver.objective.direction = obj_direction # parse groups model_groups = model.getPlugin("groups") # type: libsbml.GroupsModelPlugin groups = [] if model_groups: # calculate hashmaps to lookup objects in O(1) sid_map = {} metaid_map = {} for obj_list in [ model.getListOfCompartments(), model.getListOfSpecies(), model.getListOfReactions(), model_groups.getListOfGroups(), ]: for sbase in obj_list: # type: libsbml.SBase if sbase.isSetId(): sid_map[sbase.getIdAttribute()] = sbase if sbase.isSetMetaId(): metaid_map[sbase.getMetaId()] = sbase # create groups for group in model_groups.getListOfGroups(): # type: libsbml.Group gid = _check_required(group, group.getIdAttribute(), "id") if f_replace and F_GROUP in f_replace: gid = f_replace[F_GROUP](gid) cobra_group = Group(gid) cobra_group.name = group.getName() if group.isSetKind(): cobra_group.kind = group.getKindAsString() cobra_group.annotation = _parse_annotations(group) cobra_group.notes = _parse_notes_dict(group) cobra_members = [] for member in group.getListOfMembers(): # type: libsbml.Member if member.isSetIdRef(): obj = sid_map[member.getIdRef()] elif member.isSetMetaIdRef(): obj = metaid_map[member.getMetaIdRef()] typecode = obj.getTypeCode() obj_id = _check_required(obj, obj.getIdAttribute(), "id") # id replacements cobra_member = None if typecode == libsbml.SBML_SPECIES: if f_replace and F_SPECIE in f_replace: obj_id = f_replace[F_SPECIE](obj_id) try: cobra_member = geckopy_model.metabolites.get_by_id( obj_id) except KeyError: cobra_member = geckopy_model.proteins.get_by_id(obj_id) elif typecode == libsbml.SBML_REACTION: if f_replace and F_REACTION in f_replace: obj_id = f_replace[F_REACTION](obj_id) cobra_member = geckopy_model.reactions.get_by_id(obj_id) elif typecode == libsbml.SBML_FBC_GENEPRODUCT: if f_replace and F_GENE in f_replace: obj_id = f_replace[F_GENE](obj_id) cobra_member = geckopy_model.genes.get_by_id(obj_id) else: LOGGER.warning("Member %s could not be added to group %s." "unsupported type code: " "%s" % (member, group, typecode)) if cobra_member: cobra_members.append(cobra_member) cobra_group.add_members(cobra_members) groups.append(cobra_group) else: # parse deprecated subsystems on reactions groups_dict = {} for cobra_reaction in geckopy_model.reactions: if "SUBSYSTEM" in cobra_reaction.notes: g_name = cobra_reaction.notes["SUBSYSTEM"] if g_name in groups_dict: groups_dict[g_name].append(cobra_reaction) else: groups_dict[g_name] = [cobra_reaction] for gid, cobra_members in groups_dict.items(): if f_replace and F_GROUP in f_replace: gid = f_replace[F_GROUP](gid) cobra_group = Group(gid, name=gid, kind="collection") cobra_group.add_members(cobra_members) groups.append(cobra_group) geckopy_model.add_groups(groups) # now add everything under group Proteins to model.proteins if it was not # already added based on naming conventions if geckopy_model.groups.query("Protein"): g_proteins = geckopy_model.groups.Protein.members.copy() g_proteins = { prot: {reac: reac.metabolites[prot] for reac in prot.reactions} for prot in g_proteins if prot not in geckopy_model.proteins } if g_proteins: geckopy_model.remove_metabolites(g_proteins.keys()) geckopy_model.add_proteins([Protein(prot) for prot in g_proteins]) for prot, reactions in g_proteins.items(): for reac, stoich in reactions.items(): # reverse the (negative) stoichiometry coefficient to kcat # we expect that Proteins that are identified only by their # group are respecting the specification and do form part # of reactions # TODO: provide options to tune this behvior reac.add_protein(prot.id, -1 / (stoich * 3600)) # general hint for missing flux bounds if missing_bounds: LOGGER.warning( "Missing flux bounds on reactions set to default bounds." "As best practise and to avoid confusion flux bounds " "should be set explicitly on all reactions.") return geckopy_model