Source code for sbmlutils.parser

"""Parse Models in internal model format.

FIXME: no support for notes
FIXME: no support for modelHistory

"""
from pathlib import Path
from typing import Any, Dict, List, Optional, Union

import antimony
import libsbml
from pymetadata.omex import ManifestEntry, Omex

from sbmlutils import RESOURCES_DIR
from sbmlutils.console import console
from sbmlutils.factory import *
from sbmlutils.io.sbml import read_sbml, validate_sbml
from sbmlutils.log import get_logger
from sbmlutils.metadata import BQB, BQM
from sbmlutils.reaction_equation import EquationPart
from sbmlutils.report.sbmlinfo import SBMLDocumentInfo
from sbmlutils.validation import ValidationOptions


[docs]logger = get_logger(__name__)
[docs]def antimony_to_sbml( source: Union[Path, str], ) -> str: """Parse antimony model to SBML string.""" status: int if isinstance(source, str) and "model" in source: status = antimony.loadAntimonyString(source) else: if not isinstance(source, Path): logger.error( f"All antimony paths should be of type 'Path', but " f"'{type(source)}' found for: {source}" ) source = Path(source) status = antimony.loadAntimonyFile(str(source)) # log errors if status != -1: logger.error(f"Antimony status: {status}") logger.error(antimony.getLastError()) # antimony.getSBMLWarnings() sbml_str: str = antimony.getSBMLString() return sbml_str
[docs]def antimony_to_model( source: Union[Path, str], validate: bool = False, promote: bool = False, validation_options: Optional[ValidationOptions] = None, ) -> Model: """Parse antimony model.""" sbml_str = antimony_to_sbml( source=source, ) return sbml_to_model( source=sbml_str, validate=validate, promote=promote, validation_options=validation_options, )
# TODO: validation & validation options
[docs]def sbml_to_model( source: Union[Path, str], validate: bool = False, promote: bool = False, validation_options: Optional[ValidationOptions] = None, ) -> Model: """Parse SBML model.""" doc: libsbml.SBMLDocument = read_sbml( source=source, promote=promote, validate=validate, validation_options=validation_options, ) model: libsbml.Model = doc.getModel() def parse_sbase_kwargs(sbase: libsbml.SBase) -> Dict[str, Any]: """Parse SBase information in dictionary.""" d = SBMLDocumentInfo.sbase_dict(sbase) kwargs = { "sid": d["id"], "name": d["name"], "metaId": d["metaId"], "sboTerm": d["sbo"], "annotations": [], } # annotations if d["cvterms"]: for cvterm in d["cvterms"]: qualifier_str = cvterm["qualifier"] qualifier: Union[BQB, BQM] if qualifier_str.startswith("BQB_"): qualifier = BQB.__getitem__(qualifier_str[4:]) elif qualifier_str.startswith("BQM_"): qualifier = BQM.__getitem__(qualifier_str[4:]) for resource in cvterm["resources"]: kwargs["annotations"].append((qualifier, resource)) # model history # FIXME: currently not supported consistently, see # https: // github.com / matthiaskoenig / sbmlutils / issues / 416 # notes # FIXME: support merging of notes, see # keyValuePairs sbase_fbc: libsbml.FbcSBasePlugin = sbase.getPlugin("fbc") kvps: List[KeyValuePair] = [] if sbase_fbc: kvp: libsbml.KeyValuePair for kvp in sbase_fbc.getListOfKeyValuePairs(): kvps.append( KeyValuePair( key=kvp.getKey(), value=kvp.getValue(), uri=kvp.getUri() if kvp.isSetUri() else None, **parse_sbase_kwargs(kvp), ) ) kwargs["keyValuePairs"] = kvps # if d["notes"]: # kwargs["notes"] = d["notes"] # This is an XML string. return kwargs if not model: logger.error("No model in SBMLDocument.") m = Model(**parse_sbase_kwargs(model)) # FIXME: parse packages m.packages = [Package.FBC_V3] p: libsbml.Parameter for p in model.getListOfParameters(): d = parse_sbase_kwargs(p) # print(d) m.parameters.append( Parameter( value=p.getValue() if p.isSetValue else None, # unit=p.getUnits(), constant=p.getConstant() if p.isSetConstant() else None, **d, ) ) c: libsbml.Compartment for c in model.getListOfCompartments(): m.compartments.append( Compartment( value=c.getSize() if c.isSetSize() else NaN, constant=c.getConstant() if c.isSetConstant() else True, spatialDimensions=c.getSpatialDimensions() if c.isSetSpatialDimensions() else None, # unit=p.getUnits(), **parse_sbase_kwargs(c), ) ) s: libsbml.Species for s in model.getListOfSpecies(): m.species.append( Species( compartment=s.getCompartment() if s.isSetCompartment() else None, initialAmount=s.getInitialAmount() if s.isSetInitialAmount() else None, initialConcentration=s.getInitialConcentration() if s.isSetInitialConcentration() else None, constant=s.getConstant() if s.isSetConstant() else None, hasOnlySubstanceUnits=s.getHasOnlySubstanceUnits() if s.isSetHasOnlySubstanceUnits() else None, boundaryCondition=s.getBoundaryCondition() if s.isSetBoundaryCondition() else None, # unit=p.getUnits(), **parse_sbase_kwargs(s), ) ) # reactions r: libsbml.Reaction ast: Optional[libsbml.ASTNode] formula: Optional[str] for r in model.getListOfReactions(): # FIXME: better equation support. equation = ReactionEquation( reversible=r.getReversible() if r.isSetReversible() else None ) reactant: libsbml.SpeciesReference for reactant in r.getListOfReactants(): equation.reactants.append( EquationPart( species=reactant.getSpecies() if reactant.isSetSpecies() else None, stoichiometry=reactant.getStoichiometry() if reactant.isSetStoichiometry() else None, constant=reactant.getConstant() if reactant.isSetConstant() else True, **parse_sbase_kwargs(reactant), ) ) product: libsbml.SpeciesReference for product in r.getListOfProducts(): equation.products.append( EquationPart( species=product.getSpecies() if product.isSetSpecies() else None, stoichiometry=product.getStoichiometry() if product.isSetStoichiometry() else None, constant=product.getConstant() if product.isSetConstant() else True, **parse_sbase_kwargs(product), ) ) modifier: libsbml.SpeciesReference for modifier in r.getListOfModifiers(): if modifier.isSetSpecies(): equation.modifiers.append(modifier.getSpecies()) # formula if r.isSetKineticLaw(): klaw: libsbml.KineticLaw = r.getKineticLaw() ast = klaw.getMath() if klaw.isSetMath() else None formula = libsbml.formulaToL3String(ast) if ast else None m.reactions.append( Reaction(equation=equation, formula=formula, **parse_sbase_kwargs(r)) ) # initial assignment ia: libsbml.InitialAssignment for ia in model.getListOfInitialAssignments(): ast = ia.getMath() if ia.isSetMath() else None formula = libsbml.formulaToL3String(ast) if ast else None if formula: m.assignments.append( InitialAssignment( symbol=ia.getSymbol() if ia.isSetSymbol() else None, value=formula, **parse_sbase_kwargs(ia), ) ) # rules rule: libsbml.Rule for rule in model.getListOfRules(): ast = rule.getMath() if rule.isSetMath() else None formula = libsbml.formulaToL3String(ast) if ast else None typecode: int = rule.getTypeCode() if formula: if typecode == libsbml.SBML_ASSIGNMENT_RULE: m.rules.append( AssignmentRule( variable=rule.getVariable() if rule.isSetVariable() else None, value=formula, **parse_sbase_kwargs(rule), ) ) elif typecode == libsbml.SBML_RATE_RULE: m.rate_rules.append( RateRule( variable=rule.getVariable() if rule.isSetVariable() else None, value=formula, **parse_sbase_kwargs(rule), ) ) elif typecode == libsbml.SBML_ALGEBRAIC_RULE: m.algebraic_rules.append( AlgebraicRule(value=formula, **parse_sbase_kwargs(rule)) ) # events # constraints # FIXME: # comp # ports # fbc # groups # distrib return m
if __name__ == "__main__": from sbmlutils.resources import BIOMODELS_CURATED_PATH, REPRESSILATOR_SBML
[docs] omex_path: Path = BIOMODELS_CURATED_PATH / "BIOMD0000000003.omex"
omex = Omex().from_omex(omex_path) entry: ManifestEntry for entry in omex.manifest.entries: if entry.is_sbml(): sbml_path: Path = omex.get_path(entry.location) m = sbml_to_model(REPRESSILATOR_SBML) console.print(m) create_model( model=m, filepath=sbml_path, sbml_level=3, sbml_version=2, validation_options=ValidationOptions(units_consistency=False), ) SBMLDocumentInfo.from_sbml(sbml_path) model = sbml_to_model( source=sbml_path, validate=True, validation_options=ValidationOptions(units_consistency=False), )