Source code for simplesbml.simplesbml

# -*- coding: utf-8 -*-
"""
Created on Tue Jan 27 23:13:42 2015, 2020

@author: carolc24, Kyle Medle, hsauro
"""

import warnings

# try to import tesbml or libsbml
# if both of these fail, libsbml cannot be imported - cannot continue
try:

    import tesbml as libsbml   
except ImportError:
    import libsbml

from math import isnan
from re import sub
import os

def _isSBMLModel(obj):
  """
  Tests if object is a libsbml model
  """
  cls_stg = str(type(obj))
  if ('Model' in cls_stg) and ('lib' in cls_stg):
    return True
  else:
    return False

def _checkSBMLDocument(document, modelReference=""): 
  if (document.getNumErrors() > 0):
    raise ValueError("Errors in SBML document\n%s" % modelReference)

def _getXML(modelReference):
  """
  :param str modelReference: 
      the input may be a file reference or a model string
      or TextIOWrapper
          and the file may be an xml file or an antimony file.
      if it is a model string, it may be an xml string or antimony.
  :raises IOError: Error encountered reading the SBML document
  :return str SBML xml"
  """
  # Check for a file path
  modelStr = ""
  if isinstance(modelReference, str):
    if os.path.isfile(modelReference):
      with open(modelReference, 'r') as fd:
        lines = fd.readlines()
      modelStr = ''.join(lines)
  if len(modelStr) == 0:
    if "readlines" in dir(modelReference):
      lines = modelReference.readlines()
      if isinstance(lines[0], bytes):
        lines = [l.decode("utf-8") for l in lines]
      modelStr = ''.join(lines)
      modelReference.close()
    else:
      # Must be a string representation of a model
      modelStr = modelReference
  # Process modelStr into a model  
  if not "<sbml" in modelStr:
    # Antimony
    raise ValueError("Invalid SBML model.")
  return modelStr


[docs]class sbmlModel(object): def check(self, value, message): if value == None: raise SystemExit('LibSBML returned a null value trying to ' + \ message + '.') elif type(value) is int: if value == libsbml.LIBSBML_OPERATION_SUCCESS: return else: err_msg = 'Error trying to ' + message + '.' \ + 'LibSBML returned error code ' + str(value) + ': "'\ + libsbml.OperationReturnValue_toString(value).strip() + '"' raise RuntimeError(err_msg) else: return def __init__(self, time_units='second', extent_units='mole', \ sub_units='mole', level=3, version=1, modelReference=None): if level == 1: raise SystemExit('Error: SimpleSBML does not support SBML level 1') try: self.document = libsbml.SBMLDocument(level,version) except ValueError: raise SystemExit('Could not create SBMLDocument object') if modelReference == None: self.model = self.document.createModel() self.check(self.model, 'create model') if self.document.getLevel() == 3: self.check(self.model.setTimeUnits(time_units), 'set model-wide time units') self.check(self.model.setExtentUnits(extent_units), 'set model units of extent') self.check(self.model.setSubstanceUnits(sub_units),'set model substance units') per_second = self.model.createUnitDefinition() self.check(per_second, 'create unit definition') self.check(per_second.setId('per_second'), 'set unit definition id') unit = per_second.createUnit() self.check(unit, 'create unit on per_second') self.check(unit.setKind(libsbml.UNIT_KIND_SECOND), 'set unit kind') self.check(unit.setExponent(-1), 'set unit exponent') self.check(unit.setScale(0), 'set unit scale') self.check(unit.setMultiplier(1), 'set unit multiplier') self.addCompartment() else: xml = _getXML(modelReference) reader = libsbml.SBMLReader() document = reader.readSBMLFromString(xml) _checkSBMLDocument(document, modelReference=modelReference) self.model = document.getModel()
[docs] def addCompartment(self, vol=1, comp_id=''): c1 = self.model.createCompartment() self.check(c1, 'create compartment') if len(comp_id) == 0: comp_id = 'c' + str(self.model.getNumCompartments()) self.check(c1.setId(comp_id), 'set compartment id') self.check(c1.setConstant(True), 'set compartment "constant"') self.check(c1.setSpatialDimensions(3), 'set compartment dimensions') self.check(c1.setSize(vol), 'set compartment "size"') self.check(c1.setUnits('litre'), 'set compartment size units') return c1
[docs] def addSpecies(self, species_id, amt, comp='c1'): s1 = self.model.createSpecies() self.check(s1, 'create species s1') self.check(s1.setCompartment(comp), 'set species s1 compartment') if species_id[0] == '[' and species_id[len(species_id)-1] == ']': self.check(s1.setInitialConcentration(amt), 'set initial concentration for s1') species_id = species_id[1:(len(species_id)-1)] else: self.check(s1.setInitialAmount(amt), 'set initial amount for s1') self.check(s1.setSubstanceUnits(self.model.getSubstanceUnits()), 'set substance units for s1') if species_id[0] == '$': self.check(s1.setBoundaryCondition(True), \ 'set "boundaryCondition" on s1') self.check(s1.setConstant(False), 'set "constant" attribute on s1') self.check(s1.setId(species_id[1:len(species_id)]), 'set species s1 id') else: self.check(s1.setBoundaryCondition(False), \ 'set "boundaryCondition" on s1') self.check(s1.setConstant(False), 'set "constant" attribute on s1') self.check(s1.setId(species_id), 'set species s1 id') self.check(s1.setHasOnlySubstanceUnits(False), \ 'set "hasOnlySubstanceUnits" on s1') return s1
[docs] def addParameter(self, param_id, val, units='per_second'): k = self.model.createParameter() self.check(k, 'create parameter k') self.check(k.setId(param_id), 'set parameter k id') self.check(k.setConstant(False), 'set parameter k "not constant"') self.check(k.setValue(val), 'set parameter k value') self.check(k.setUnits(units), 'set parameter k units') return k
[docs] def addReaction(self, reactants, products, expression, local_params={}, rxn_id=''): r1 = self.model.createReaction() self.check(r1, 'create reaction') if len(rxn_id) == 0: rxn_id = 'v' + str(self.model.getNumReactions()) self.check(r1.setId(rxn_id), 'set reaction id') self.check(r1.setReversible(False), 'set reaction reversibility flag') self.check(r1.setFast(False), 'set reaction "fast" attribute') for re in reactants: if re is not None and '$' in re: re.translate(None, '$') re_split = re.split() if len(re_split) == 1: sto = 1.0 re_id = re elif len(re_split) == 2 and re_split[0].isdigit(): sto = float(re_split[0]) re_id = re_split[1] else: err_msg = 'Error: reactants must be listed in format \'S\' or \'(float)\' S\'' raise SystemExit(err_msg) s1 = self.model.getSpecies(re_id) species_ref1 = r1.createReactant() self.check(species_ref1, 'create reactant') self.check(species_ref1.setSpecies(s1.getId()), \ 'assign reactant species') self.check(species_ref1.setStoichiometry(sto), \ 'assign reactant stoichiometry') if self.document.getLevel() == 3: self.check(species_ref1.setConstant(True), \ 'set "constant" on species ref 1') for pro in products: if pro is not None and '$' in pro: pro.translate(None, '$') pro_split = pro.split() if len(pro_split) == 1: sto = 1.0 pro_id = pro elif len(pro_split) == 2: sto = float(pro_split[0]) pro_id = pro_split[1] else: err_msg = 'Error: products must be listed in format \'S\' or \'(float)\' S\'' raise SystemExit(err_msg) s2 = self.model.getSpecies(pro_id) species_ref2 = r1.createProduct() self.check(species_ref2, 'create product') self.check(species_ref2.setSpecies(s2.getId()), \ 'assign product species') self.check(species_ref2.setStoichiometry(sto), \ 'set product stoichiometry') if self.document.getLevel() == 3: self.check(species_ref2.setConstant(True), \ 'set "constant" on species ref 2') math_ast = libsbml.parseL3Formula(expression) self.check(math_ast, 'create AST for rate expression') kinetic_law = r1.createKineticLaw() self.check(kinetic_law, 'create kinetic law') self.check(kinetic_law.setMath(math_ast), 'set math on kinetic law') for param in local_params.keys(): val = local_params.get(param) if self.document.getLevel() == 3: p = kinetic_law.createLocalParameter() else: p = kinetic_law.createParameter() self.check(p, 'create local parameter') self.check(p.setId(param), 'set id of local parameter') self.check(p.setValue(val), 'set value of local parameter') return r1
[docs] def addEvent(self, trigger, assignments, persistent=True, \ initial_value=False, priority=0, delay=0, event_id=''): e1 = self.model.createEvent() self.check(e1, 'create event') if len(event_id) == 0: event_id = 'e' + str(self.model.getNumEvents()) self.check(e1.setId(event_id), 'add id to event') if self.document.getLevel()==3 or (self.document.getLevel()==2 \ and self.document.getVersion()==4): self.check(e1.setUseValuesFromTriggerTime(True), 'set use values from trigger time') tri = e1.createTrigger() self.check(tri, 'add trigger to event') tri_ast = libsbml.parseL3Formula(trigger) self.check(tri.setMath(tri_ast), 'add formula to trigger') if self.document.getLevel() == 3: self.check(tri.setPersistent(persistent), 'set persistence of trigger') self.check(tri.setInitialValue(initial_value), 'set initial value of trigger') de = e1.createDelay() if self.document.getLevel() == 3: k = self.addParameter(event_id+'Delay', delay, self.model.getTimeUnits()) else: k = self.addParameter(event_id+'Delay', delay, 'time') self.check(de, 'add delay to event') delay_ast = libsbml.parseL3Formula(k.getId()) self.check(de.setMath(delay_ast), 'set formula for delay') for a in assignments.keys(): assign = e1.createEventAssignment() self.check(assign, 'add event assignment to event') self.check(assign.setVariable(a), 'add variable to event assignment') val_ast = libsbml.parseL3Formula(assignments.get(a)) self.check(assign.setMath(val_ast), 'add value to event assignment') if self.document.getLevel() == 3: pri = e1.createPriority() pri_ast = libsbml.parseL3Formula(str(priority)) self.check(pri.setMath(pri_ast), 'add priority to event') return e1
[docs] def addAssignmentRule(self, var, math): r = self.model.createAssignmentRule() self.check(r, 'create assignment rule r') self.check(r.setVariable(var), 'set assignment rule variable') math_ast = libsbml.parseL3Formula(math) self.check(r.setMath(math_ast), 'set assignment rule equation') return r
[docs] def addRateRule(self, var, math): r = self.model.createRateRule() self.check(r, 'create rate rule r') self.check(r.setVariable(var), 'set rate rule variable') math_ast = libsbml.parseL3Formula(math) self.check(r.setMath(math_ast), 'set rate rule equation') return r
[docs] def addInitialAssignment(self, symbol, math): if self.document.getLevel() == 2 and self.document.getVersion() == 1: raise SystemExit('Error: InitialAssignment does not exist for \ this level and version.') a = self.model.createInitialAssignment() self.check(a, 'create initial assignment a') self.check(a.setSymbol(symbol), 'set initial assignment a symbol') math_ast = libsbml.parseL3Formula(math) self.check(a.setMath(math_ast), 'set initial assignment a math') return a
def setLevelAndVersion(self, level, version): if level == 2 and version == 1: self.check(self.document.checkL2v1Compatibility(), 'convert to level 2 version 1') elif level == 2 and version == 2: self.check(self.document.checkL2v2Compatibility(), 'convert to level 2 version 2') elif level == 2 and version == 3: self.check(self.document.checkL2v3Compatibility(), 'convert to level 2 version 3') elif level == 2 and version == 4: self.check(self.document.checkL2v4Compatibility(), 'convert to level 2 version 4') elif level == 3 and version == 1: self.check(self.document.checkL3v1Compatibility(), 'convert to level 3 version 1') else: raise SystemExit('Invalid level/version combination') isSet = self.document.setLevelAndVersion(level, version) self.check(isSet, 'convert to level ' + str(level) + ' version ' + str(version))
[docs] def getDocument(self): return self.document
[docs] def getModel(self): return self.model
def getNumCompartments (self): """ Returns the number of compartments in the current model """ return self.model.getNumCompartments() def getNumSpecies (self): return self.model.getNumSpecies() def getNumParameters (self): return self.model.getNumParameters() def getNumReactions (self): return self.model.getNumReactions() def getNumEvents (self): return self.model.getNumEvents() def getNumRules (self): return self.model.getNumRules() def getNumFunctionDefinitions (self): return self.model.getNumFunctionDefinitions() def getNumInitialAssignments (self): return self.model.getNumInitialAssignments() def getSpecies(self, species_id): return self.model.getSpecies(species_id) def getListOfCompartments(self): alist = [] nCompartments = self.model.getNumCompartments() for i in range (nCompartments): comp = self.model.getCompartment(i) alist.append (comp.getId()) return alist def getCompartmentVolume (self, Id): p = self.model.getCompartment(Id) if p != None: return p.getVolume() raise Exception ('Compartment does not exist') def getListOfAllSpecies(self): alist = [] nSpecies = self.model.getNumSpecies() for i in range (nSpecies): sp = self.model.getSpecies(i) alist.append (sp.getId()) return alist def getSpeciesInitialConcentration (self, Id): p = self.model.getSpecies(Id) if p != None: return p.getInitialConcentration() raise Exception ('Species does not exist') def getSpeciesInitialAmount (self, Id): p = self.model.getSpecies(Id) if p != None: return p.getInitialAmount() raise Exception ('Species does not exist') def getListOfFloatingSpecies(self): alist = [] nSpecies = self.model.getNumSpecies() for i in range (nSpecies): sp = self.model.getSpecies(i) if not sp.getBoundaryCondition(): alist.append (sp.getId()) return alist def getListOfBoundarySpecies(self): alist = [] nSpecies = self.model.getNumSpecies() for i in range (nSpecies): sp = self.model.getSpecies(i) if sp.getBoundaryCondition(): alist.append (sp.getId()) return alist def getNumFloatingSpecies (self): return len (self.getListOfFloatingSpecies()) def getNumBoundarySpecies (self): return len (self.getListOfBoundarySpecies()) #def getParameter(self, param_id): # return self.model.getParameter(param_id) def getListOfParameters(self): alist = [] nParameters = self.model.getNumParameters() for i in range (nParameters): p = self.model.getParameter(i) alist.append (p.getId()) return alist def getParameterValue (self, Id): p = self.model.getParameter(Id) if p != None: return p.getValue() raise Exception ('Parameter does not exist') def getListOfReactions(self): alist = [] nReactions = self.model.getNumReactions() for i in range (nReactions): p = self.model.getReaction(i) alist.append (p.getId()) return alist def getNumReactants (self, Id): p = self.model.getReaction(Id) if p != None: return p.getNumReactants() raise Exception ('Reaction does not exist') def getNumProducts (self, Id): p = self.model.getReaction(Id) if p != None: return p.getNumProducts() raise Exception ('Reaction does not exist') def getRateLaw (self, Id): p = self.model.getReaction(Id) if p != None: return p.getKineticLaw().getFormula() raise Exception ('Reaction does not exist') def getReactant (self, reactionId, reactantIndex): ra = self.model.getReaction(reactionId) sr = ra.getReactant(reactantIndex) return sr.getSpecies() def getProduct (self, reactionId, productIndex): ra = self.model.getReaction(reactionId) sr = ra.getProduct(productIndex) return sr.getSpecies() def getReactantStoichiometry (self, reactionId, reactantIndex): ra = self.model.getReaction(reactionId) sr = ra.getReactant(reactantIndex) return sr.getStoichiometry () def getProductStoichiometry (self, reactionId, productIndex): ra = self.model.getReaction(reactionId) sr = ra.getProduct(productIndex) return sr.getStoichiometry () def getListOfRules(self): alist = [] nRules = self.model.getNumRules() for i in range (nRules): p = self.model.getRule(i) alist.append (p.getId()) return alist # def getReaction(self, rxn_id): # return self.model.getReaction(rxn_id) # def getCompartment(self, comp_id): # return self.model.getCompartment(comp_id) # def getListOfEvents(self): # return self.model.getListOfEvents() # def getEvent(self, event_id): # return self.model.getEvent(event_id) # def getRule(self, var): # return self.model.getRule(var) # def getInitialAssignment(self, var): # return self.model.getInitialAssignment(var) # def getListOfInitialAssignments(self): # return self.model.getListOfInitialAssignments()
[docs] def toSBML(self): errors = self.document.checkConsistency() if (errors > 0): for i in range(errors): print(self.document.getError(i).getSeverityAsString(), ": ", self.document.getError(i).getMessage()) return libsbml.writeSBMLToString(self.document)
def __repr__(self): return self.toSBML()
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: warnings.warn('Warning: SimpleSBML does not support SBML Level 1.') 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 writeCodeFromFile(filename): reader = libsbml.SBMLReader() doc = reader.readSBMLFromFile(filename) if doc.getNumErrors() > 0: raise SystemExit(doc.getError(0)) return writeCode(doc) def writeCodeFromString(sbmlstring): reader = libsbml.SBMLReader() doc = reader.readSBMLFromString(sbmlstring) return writeCode(doc)