Source code for tellurium.teconverters.antimony_sbo

"""
Helpers to work with SBO terms in antimony.
"""

from __future__ import print_function, division, absolute_import

import re
try:
    import libsbml
except ImportError:
    import tesbml as libsbml

from .antimony_regex import getModelStartRegex, getModelEndRegex, getFunctionStartRegex, getSBORegex, getFunctionSBORegex

[docs] def filterIfEmpty(l): """ If l is one line (comment), filter.""" if len(l) == 1: return [] else: return l
[docs] class SBOError(RuntimeError): pass
[docs] class antimonySBOConverter(object): def __init__(self, doc): """ Create an SBO converter. :param doc: SBMLDocument """ self.doc = doc if self.doc.getNumErrors() > 0: raise RuntimeError('Errors encountered loading SBML') self.model = doc.getModel() if self.model is None: raise RuntimeError('No SBML model')
[docs] def convert(self, antimony_str): """ Add SBO terms to the Antimony string corresponding to this SBML document. :param antimony_str: Antimony string. Should represent the same SBML this object was initialized with. :return: The Antimony string with the SBO terms added. """ model_start = re.compile(getModelStartRegex()) n_model_starts = 0 function_start = re.compile(getFunctionStartRegex()) model_end = re.compile(getModelEndRegex()) n_model_ends = 0 model_end_index = None scopes = [] n_leading_spaces = 0 lines = antimony_str.splitlines() for n,line in enumerate(lines): if model_start.match(line) != None: n_model_starts += 1 scopes.append('model') if function_start.match(line) != None: scopes.append('function') elif model_end.match(line) != None: if n_model_ends > 0: raise SBOError('Multiple embedded models (e.g. comp) not supported for SBO converter') if len(scopes) == 0: raise RuntimeError('Unbalanced begin/end blocks') scope = scopes.pop() if scope == 'model': n_model_ends += 1 model_end_index = n elif scope == 'function': pass else: raise RuntimeError('Unknown scope') else: # calculate leading whitespace ws = len(line) - len(line.lstrip(' ')) if ws > n_leading_spaces and n_leading_spaces == 0: n_leading_spaces = ws if n_model_starts != n_model_ends: raise RuntimeError('Antimony model begin/end blocks unbalanced - missing begin/end marker?') if n_model_starts > 1: raise SBOError('Multiple embedded models (e.g. comp) not supported for SBO converter') if n_model_ends > 1: raise SBOError('Multiple embedded models (e.g. comp) not supported for SBO converter') # add SBO terms right before the end lead_space = ' '*n_leading_spaces if model_end_index is not None: # get all sbo terms sbo_terms = self.getAllSBOTerms() if sbo_terms: lines[model_end_index:model_end_index] = ['', lead_space + '// SBO terms:'] + \ [lead_space + term for term in sbo_terms] + [''] return '\n'.join(lines)
[docs] def getAllSBOTerms(self): """ Get a list of all SBO terms base on the SBML passed to the constructor. """ comps = self.getCompartmentSBOTerms() species = self.getSpeciesSBOTerms() params = self.getParameterSBOTerms() rxns = self.getReactionSBOTerms() return comps + species + params + rxns
[docs] def getCompartmentSBOTerms(self): return filterIfEmpty(['// - Compartment SBO Terms:'] + \ [self.createSBOTermStringForElt(e) for e in (self.model.getCompartment(k) for k in range(self.model.getNumCompartments())) \ if self.hasSBOTerm(e)])
[docs] def getSpeciesSBOTerms(self): return filterIfEmpty(['// - Species SBO Terms:'] + \ [self.createSBOTermStringForElt(e) for e in (self.model.getSpecies(k) for k in range(self.model.getNumSpecies())) \ if self.hasSBOTerm(e)])
[docs] def getParameterSBOTerms(self): return filterIfEmpty(['// - Parameter SBO Terms:'] + \ [self.createSBOTermStringForElt(e) for e in (self.model.getParameter(k) for k in range(self.model.getNumParameters())) \ if self.hasSBOTerm(e)])
[docs] def getReactionSBOTerms(self): return filterIfEmpty(['// - Reaction SBO Terms:'] + \ [self.createSBOTermStringForElt(e) for e in (self.model.getReaction(k) for k in range(self.model.getNumReactions())) \ if self.hasSBOTerm(e)])
[docs] def hasSBOTerm(self, elt): if elt.isSetSBOTerm(): return True else: return False
[docs] def createSBOTermStringForElt(self, elt): if elt.isSetSBOTerm(): return elt.getId() + '.sboTerm = ' + 'SBO:{:07d};'.format(self.getSBOTermForElt(elt)) else: return None
[docs] def getSBOTermForElt(self, elt): if elt.isSetSBOTerm(): return elt.getSBOTerm() else: raise RuntimeError('No SBO term set.')
[docs] @classmethod def fromSBMLFile(cls, sbml_file): """ Construct from SBML file. """ reader = libsbml.SBMLReader() doc = reader.readSBMLFromFile(sbml_file) return antimonySBOConverter(doc)
[docs] @classmethod def fromSBMLString(cls, sbml_str): """ Construct from SBML string. """ reader = libsbml.SBMLReader() doc = reader.readSBMLFromString(sbml_str) return antimonySBOConverter(doc)
[docs] class antimonySBOParser(object): def __init__(self, antimony_str): """Initialize from an Antimony string.""" self.antimony_str = antimony_str
[docs] def elideSBOTerms(self): """Remove SBO terms from self.antimony_str. Remove SBO terms for functions. See https://github.com/sys-bio/tellurium/issues/340. :return: Antimony string without SBO terms.""" self.sbo_map = {} model_start = re.compile(getModelStartRegex()) n_model_starts = 0 model_end = re.compile(getModelEndRegex()) n_model_ends = 0 model_end_index = None sbo_term = re.compile(getSBORegex()) fct_start = re.compile(getFunctionStartRegex()) fct_end = re.compile(getModelEndRegex()) function_sbo = re.compile(getFunctionSBORegex()) fct_id = '' n_functions = 0 in_function = False n_leading_spaces = 0 lines = self.antimony_str.splitlines() out_lines = [] for n,line in enumerate(lines): sbo_match = sbo_term.match(line) fct_match = fct_start.match(line) fct_sbo_match = function_sbo.match(line) if model_start.match(line) != None: if n_model_starts > 0: raise SBOError('Multiple embedded models (e.g. comp) not supported for SBO converter') n_model_starts += 1 out_lines.append(line) elif model_end.match(line) != None: if in_function: in_function = False fct_id = '' else: n_model_ends += 1 model_end_index = n out_lines.append(line) elif fct_match != None: if not in_function: n_functions += 1 in_function = True out_lines.append(line) fct_id = fct_match.group(1) #print('Match function id {}'.format(fct_id)) else: raise RuntimeError('Nested function: {}'.format(line)) elif sbo_term.match(line): elt_id = sbo_match.group(1) sbo = int(sbo_match.group(3)) self.sbo_map[elt_id] = sbo #print('Match SBO term {}->{}'.format(elt_id,sbo)) elif fct_sbo_match != None: sbo = int(fct_sbo_match.group(2)) #self.sbo_map[elt_id] = sbo #print('Match function SBO term {}->{}'.format(fct_id,sbo)) self.sbo_map[fct_id] = sbo else: out_lines.append(line) if n_model_starts != n_model_ends: raise RuntimeError('Antimony model begin/end blocks unbalanced - missing begin/end marker?') if n_model_starts > 1: raise SBOError('Multiple embedded models (e.g. comp) not supported for SBO converter') if n_model_ends > 1: raise SBOError('Multiple embedded models (e.g. comp) not supported for SBO converter') return '\n'.join(out_lines)
[docs] def addSBOsToSBML(self, sbml_str): """Add SBO terms to an SBML string. Must have called elideSBOTerms first to populate self.sbo_map.""" reader = libsbml.SBMLReader() doc = reader.readSBMLFromString(sbml_str) if doc.getNumErrors() > 0: raise RuntimeError('Errors reading SBML') for elt_id,sbo in self.sbo_map.items(): elt = doc.getElementBySId(elt_id) if elt is not None: # print('set {} -> {}'.format(elt.getId(), sbo)) elt.setSBOTerm(sbo) writer = libsbml.SBMLWriter() out_sbml = writer.writeSBMLToString(doc) return out_sbml