Source code for pyenzyme.enzymeml.tools.enzymemlwriter

# File: enzymemlwriter.py
# Project: tools
# Author: Jan Range
# License: BSD-2 clause
# Copyright (c) 2022 Institute of Biochemistry and Technical Biochemistry Stuttgart

import pandas as pd
import os
import shutil
import libsbml
import tempfile
import logging
import warnings

from libsbml import (
    SBMLDocument,
    XMLNode,
    XMLTriple,
    XMLAttributes,
    XMLNamespaces,
    SBMLWriter,
)

from typing import Dict, List, Callable, Optional
from libcombine import CombineArchive, OmexDescription, KnownFormats, VCard

from pyenzyme.enzymeml.core.unitdef import UnitDef
from pyenzyme.enzymeml.core.vessel import Vessel
from pyenzyme.enzymeml.core.abstract_classes import AbstractSpecies
from pyenzyme.enzymeml.core.protein import Protein
from pyenzyme.enzymeml.core.reactant import Reactant
from pyenzyme.enzymeml.core.replicate import Replicate
from pyenzyme.enzymeml.core.measurement import Measurement
from pyenzyme.enzymeml.core.measurementData import MeasurementData
from pyenzyme.enzymeml.core.enzymereaction import EnzymeReaction, ReactionElement
from pyenzyme.enzymeml.models.kineticmodel import KineticModel, KineticParameter

# Initialize the logger
logger = logging.getLogger("pyenzyme")


[docs]class EnzymeMLWriter: def __init__(self): self.namespace = "http://sbml.org/enzymeml/version2"
[docs] def toFile(self, enzmldoc, path: str, name: Optional[str] = None): """ Writes EnzymeMLDocument object to an .omex container Args: enzmldoc (String): Previously created instance of an EnzymeML document path (String): EnzymeML file is written to this destination name (String): Name of the target EnzymeML file. Defaults to 'None' and thus uses the doc name. """ self.path = os.path.normpath(path) self.enzmldoc = enzmldoc # TODO replace all static enzmldocs with self.enzmldoc try: os.makedirs(os.path.join(self.path, "data")) except FileExistsError: pass doc = SBMLDocument() doc.setLevelAndVersion(3, 2) model = doc.createModel() model.setName(enzmldoc.name) model.setId(enzmldoc.name) # Convert the SBML model to EnzymeML self.convertEnzymeMLToSBML(model, enzmldoc) # Add data paths = self._addData(model, enzmldoc.measurement_dict) # Write to EnzymeML writer = SBMLWriter() writer.writeSBMLToFile(doc, os.path.join(self.path, "experiment.xml")) # Validate the SBML document self._validate_sbml_doc(document=doc) # Write to OMEX self._createArchive(enzmldoc, paths, name) shutil.rmtree(os.path.join(self.path, "data"), ignore_errors=True) os.remove(os.path.join(self.path, "experiment.xml"))
[docs] def toXMLString(self, enzmldoc): """ Converts EnzymeMLDocument to XML string. Args: EnzymeMLDocument enzmldoc: Previously created instance of an EnzymeML document """ doc = SBMLDocument() doc.setLevelAndVersion(3, 2) model = doc.createModel() model.setName(enzmldoc.name) model.setId(enzmldoc.name) self.path = None self.enzmldoc = enzmldoc # Convert the SBML model to EnzymeML self.convertEnzymeMLToSBML(model, enzmldoc) # Add data self._addData(model, enzmldoc.measurement_dict) # Write to EnzymeML writer = SBMLWriter() return writer.writeToString(doc)
[docs] def toSBML(self, enzmldoc): """ Returns libSBML model. Args: EnzymeMLDocument enzmldoc: Previously created instance of an EnzymeML document """ doc = SBMLDocument() doc.setLevelAndVersion(3, 2) model = doc.createModel() model.setName(enzmldoc.name) model.setId(enzmldoc.name) # Convert the SBML model to EnzymeML self.convertEnzymeMLToSBML(model, enzmldoc) return doc
[docs] def convertEnzymeMLToSBML(self, model: libsbml.Model, enzmldoc): """Manages the conversion of EnzymeML to SBML. Args: model (libsbml.Model): The blank SBML model, where the EnzymeML document is converted to. enzmldoc (EnzymeMLDocument): The EnzymeML document to be converted. """ self._addRefs(model, enzmldoc) self._addVessel(model, enzmldoc.vessel_dict) self._addProteins(model, enzmldoc.protein_dict) self._addComplex(model, enzmldoc.complex_dict) self._addReactants(model, enzmldoc.reactant_dict) self._addGlobalParameters(model, enzmldoc.global_parameters) self._addReactions(model, enzmldoc.reaction_dict) self._addUnits(model, enzmldoc._unit_dict)
def _createArchive(self, enzmldoc, listofPaths, name: str = None): archive = CombineArchive() # add experiment file to archive archive.addFile( f"{self.path}/experiment.xml", "./experiment.xml", KnownFormats.lookupFormat("sbml"), True, ) # add logs to teh Archive history_path = f"{self.path}/history.log" try: with open(history_path, "wb") as f: f.write(enzmldoc.log.getvalue().encode("utf-8", "replace")) self.addFileToArchive( archive=archive, file_path=history_path, target_path="./history.log", format=KnownFormats.lookupFormat("txt"), description="History of the EnzymeML document", ) except Exception as e: logging.error("couldn't write history") # add metadata to the experiment file location = "./experiment.xml" description = OmexDescription() description.setAbout(location) description.setDescription("EnzymeML model") description.setCreated(OmexDescription.getCurrentDateAndTime()) for creat in enzmldoc.creator_dict.values(): creator = VCard() creator.setFamilyName(creat.family_name) creator.setGivenName(creat.given_name) creator.setEmail(creat.mail) description.addCreator(creator) archive.addMetadata(".", description) archive.addMetadata(location, description) # Add CSV files to archive for csvPath, file_path in listofPaths.items(): self.addFileToArchive( archive=archive, file_path=csvPath, target_path=file_path, format=KnownFormats.lookupFormat("csv"), description="Time course data", ) # Add files from fileDict tmpFolder = None if enzmldoc.file_dict: # create temporary directory for files tmpFolder = tempfile.mkdtemp() for fileDict in enzmldoc.file_dict.values(): file_handler = fileDict["handler"] file_name = fileDict["name"] fileDescription = fileDict["description"] tmpPath = os.path.join(tmpFolder, file_name) # Write file locally and add it to the document with open(tmpPath, "wb") as fileHandle: fileHandle.write(file_handler.read()) self.addFileToArchive( archive=archive, file_path=tmpPath, target_path=f"./files/{file_name}", format=KnownFormats.guessFormat(file_name), description=fileDescription, ) if name: out_file = f"{name.replace(' ', '_')}.omex" else: out_file = f"{enzmldoc.name.replace(' ', '_')}.omex" out_path = os.path.join(self.path, out_file) try: os.remove(out_path) except FileNotFoundError: pass archive.writeToFile(out_path) # Remove temporary directory if tmpFolder is not None: shutil.rmtree(tmpFolder, ignore_errors=True) # Remove unused os.remove(f"{self.path}/history.log") print(f"\nArchive was written to {out_path}\n")
[docs] @staticmethod def addFileToArchive(archive, file_path, target_path, format, description): # Add file to archive archive.addFile(file_path, target_path, format, False) # Add metadata to the file omexDesc = OmexDescription() omexDesc.setAbout(target_path) omexDesc.setDescription(description) omexDesc.setCreated(OmexDescription.getCurrentDateAndTime()) archive.addMetadata(target_path, omexDesc)
[docs] def setupXMLNode(self, name, namespace=True): # Helper function # Creates an XML node # for annotations node = XMLNode(XMLTriple(name), XMLAttributes(), XMLNamespaces()) if namespace is True: node.addNamespace(self.namespace, "enzymeml") return node
[docs] @staticmethod def appendAttribute(attributeName, value): # Helper function # creates XML node # <x>XYZ</x> value = XMLNode(value) attributeNode = XMLNode( XMLTriple(attributeName), XMLAttributes(), XMLNamespaces() ) attributeNode.addChild(value) return attributeNode
[docs] def appendMultiAttributes( self, attributeName, object, objectMapping, annotationNode ): node = self.setupXMLNode(attributeName, namespace=False) for key, value in objectMapping.items(): # "value" --> 10.00 if hasattr(object, value): attribute = getattr(object, value) if attribute: node.addAttr(key, str(attribute)) else: return 0 else: return 0 annotationNode.addChild(node)
[docs] def appendOptionalAttribute( self, attributeName, object, objectName, annotationNode ): # Helper function # Adds elements to annotation node if object.__dict__.get(objectName): value = self.appendAttribute( attributeName, str(getattr(object, objectName)) ) annotationNode.addChild(value)
def _addRefs(self, model: libsbml.Model, enzmldoc): """Converts EnzymeMLDocument refrences to SBML. Args: model (libsbml.Model): The SBML model the reference is added to. enzmldoc (EnzymeMLDocument): The EnzymeMLDocument instance that contains the information. """ # Create reference node referenceAnnotation = self.setupXMLNode("enzymeml:references") # Optional attributes referenceAttributes = { "enzymeml:doi": "doi", "enzymeml:pubmedID": "pubmedid", "enzymeml:url": "url", } for attributeName, objectName in referenceAttributes.items(): self.appendOptionalAttribute( attributeName=attributeName, object=enzmldoc, objectName=objectName, annotationNode=referenceAnnotation, ) if referenceAnnotation.getNumChildren() > 0: model.appendAnnotation(referenceAnnotation) def _addUnits(self, model: libsbml.Model, unit_dict: Dict[str, UnitDef]) -> None: """Converts EnzymeMLDocument units to SBML. Args: model (libsbml.Model): The SBML model the units are added to. unit_dict (Dict[str, UnitDef]): The EnzymeMLDocument units to be added to the SBML model. """ for unit_id, unit_def in unit_dict.items(): unit = model.createUnitDefinition() unit.setId(unit_id) unit.setMetaId(unit_def.meta_id) unit.setName(unit_def.name) # TODO Add ontology # if unit_def.getOntology() != "NONE": # cvterm = CVTerm() # cvterm.addResource(unit_def.getOntology()) # cvterm.setQualifierType(BIOLOGICAL_QUALIFIER) # cvterm.setBiologicalQualifierType(BQB_IS) # unit.addCVTerm(cvterm) for base_unit in unit_def.units: kind = base_unit.kind exponent = base_unit.exponent scale = base_unit.scale multiplier = base_unit.multiplier baseUnitDef = unit.createUnit() try: baseUnitDef.setKind(libsbml.UnitKind_forName(kind)) except TypeError: baseUnitDef.setKind(kind) baseUnitDef.setExponent(exponent) baseUnitDef.setScale(scale) baseUnitDef.setMultiplier(multiplier) def _addVessel(self, model, vessel_dict: Dict[str, Vessel]) -> None: """Converts EnzymeMLDocument vessel to SBML. Args: model (libsbml.Model): The SBML model the vessel is added to. vessel (Vessel): The EnzymeMLDocument vessel to be added to the SBML model. """ for vessel_id, vessel in vessel_dict.items(): compartment = model.createCompartment() compartment.setId(vessel_id) compartment.setName(vessel.name) compartment.setConstant(vessel.constant) compartment.setSpatialDimensions(3) if vessel.volume: compartment.setSize(vessel.volume) compartment.setUnits(vessel._unit_id) def _addProteins( self, model: libsbml.Model, protein_dict: Dict[str, Protein] ) -> None: """Converts EnzymeMLDocument proteins to SBML. Args: model (libsbml.Model): The SBML model the proteins are added to. protein_dict (Dict[str, Protein]): The EnzymeMLDocument proteins to be added to the SBML model. """ # EnzymeML attributes proteinAttributes = { "enzymeml:sequence": "sequence", "enzymeml:ECnumber": "ecnumber", "enzymeml:uniprotID": "uniprotid", "enzymeml:organism": "organism", "enzymeml:organismTaxID": "organism_tax_id", } for protein in protein_dict.values(): self._addSpecies( obj=protein, annotation_name="enzymeml:protein", model=model, optional_attributes=proteinAttributes, ) def _addComplex( self, model: libsbml.Model, complex_dict: Dict[str, Protein] ) -> None: """Converts EnzymeMLDocument proteins to SBML. Args: model (libsbml.Model): The SBML model the proteins are added to. complex_dict (Dict[str, Protein]): The EnzymeMLDocument complex to be added to the SBML model. """ complexAttributes = { "enzymeml:participant": "participants", } for complex in complex_dict.values(): self._addSpecies( obj=complex, annotation_name="enzymeml:complex", model=model, optional_attributes=complexAttributes, ) def _addReactants(self, model: libsbml.Model, reactant_dict: Dict[str, Reactant]): """Converts EnzymeMLDocument reactants to SBML. Args: model (libsbml.Model): The SBML model the reactants are added to. reactant_dict (Dict[str, Reactant]): The EnzymeMLDocument reactants to be added to the SBML model. """ # EnzymeML attributes reactantAttributes = { "enzymeml:inchi": "inchi", "enzymeml:smiles": "smiles", "enzymeml:chebiID": "chebi_id", } for reactant in reactant_dict.values(): self._addSpecies( obj=reactant, annotation_name="enzymeml:reactant", model=model, optional_attributes=reactantAttributes, ) def _addGlobalParameters( self, model: libsbml.Model, global_parameters: Dict[str, KineticParameter] ): """Writes global parameters to the SBML document. Args: model (libsbml.Model): SBML model the global parameters are added to. global_parameters (Dict[str, KineticParameter]): Global parameters that will be added to the SBML document. """ for parameter in global_parameters.values(): sbml_param = self._write_global_parameter(parameter, model) # Create a mapping for the 'enzymeml:parameter' annotation annotation = self.setupXMLNode("enzymeml:parameter") optional_parameters = { "enzymeml:initialValue": "initial_value", "enzymeml:upperBound": "upper", "enzymeml:lowerBound": "lower", } for attributeName, objectName in optional_parameters.items(): self.appendOptionalAttribute( attributeName=attributeName, object=parameter, objectName=objectName, annotationNode=annotation, ) if annotation.getNumChildren() > 0: sbml_param.appendAnnotation(annotation) def _write_global_parameter( self, parameter: KineticParameter, model: libsbml.Model ): """Writes a global parameter to a reaction""" global_param = model.createParameter() global_param.setId(parameter.name) global_param.setConstant(parameter.constant) if parameter.value is not None: global_param.setValue(parameter.value) if parameter._unit_id: global_param.setUnits(parameter._unit_id) if parameter.ontology: global_param.setSBOTerm(parameter.ontology) return global_param def _addSpecies( self, obj: AbstractSpecies, annotation_name: str, model: libsbml.Model, optional_attributes: Dict[str, str], ) -> None: """Helper function to create any EnzymeML species from Args: obj (AbstractSpecies): Object containing the species informations. """ species = model.createSpecies() species.setId(obj.id) species.setName(obj.name) species.setMetaId(obj.meta_id) species.setSBOTerm(obj.ontology) species.setCompartment(obj.vessel_id) species.setBoundaryCondition(obj.boundary) species.setConstant(obj.constant) species.setHasOnlySubstanceUnits(False) if obj.init_conc is not None and obj._unit_id: species.setSubstanceUnits(obj._unit_id) species.setInitialConcentration(obj.init_conc) elif obj.init_conc and not obj._unit_id: raise ValueError( f"The object {obj.name} ({obj.id}) has an initial concentration value but no unit. Please specify a unit for a successful export to SBML." ) # Controls if annotation will be added objAnnotation = self.setupXMLNode(annotation_name) # EnzymeML attributes for attributeName, objectName in optional_attributes.items(): # TODO more elegant way to handle complex parts if isinstance(getattr(obj, objectName), list): for id in getattr(obj, objectName): value = self.appendAttribute(attributeName, id) objAnnotation.addChild(value) continue self.appendOptionalAttribute( attributeName=attributeName, object=obj, objectName=objectName, annotationNode=objAnnotation, ) if objAnnotation.getNumChildren() > 0: species.appendAnnotation(objAnnotation) def _addReactions( self, model: libsbml.Model, reaction_dict: Dict[str, EnzymeReaction] ): """Converts EnzymeMLDocument reactions to SBML. Args: model (libsbml.Model): The SBML model the reactions are added to. reaction_dict (Dict[str, EnzymeReaction]): The EnzymeMLDocument reactions to be added to the SBML model. """ for reaction_id, enzyme_reaction in reaction_dict.items(): reaction = model.createReaction() reaction.setId(reaction_id) reaction.setMetaId(enzyme_reaction.meta_id) reaction.setName(enzyme_reaction.name) reaction.setReversible(enzyme_reaction.reversible) reaction.setSBOTerm(enzyme_reaction.ontology) # Add kinetic model if enzyme_reaction.model: self._addModelToReaction( reaction=reaction, kinetic_model=enzyme_reaction.model ) # Enzymeml attributes reactionAnnotation = self.setupXMLNode("enzymeml:reaction") # Track conditions conditionsAnnotation = self.setupXMLNode( "enzymeml:conditions", namespace=False ) conditionsMapping = { "enzymeml:temperature": { "value": "temperature", "unit": "_temperature_unit_id", }, "enzymeml:ph": {"value": "ph"}, } for attributeName, objectMapping in conditionsMapping.items(): self.appendMultiAttributes( attributeName=attributeName, object=enzyme_reaction, objectMapping=objectMapping, annotationNode=conditionsAnnotation, ) if conditionsAnnotation.getNumChildren() > 0: reactionAnnotation.addChild(conditionsAnnotation) reaction.appendAnnotation(reactionAnnotation) # Write educts self.writeElements( reaction_elements=enzyme_reaction.educts, createFunction=reaction.createReactant, ) # Write products self.writeElements( reaction_elements=enzyme_reaction.products, createFunction=reaction.createProduct, ) # Write modifiers self.writeElements( reaction_elements=enzyme_reaction.modifiers, createFunction=reaction.createModifier, ) def _addModelToReaction( self, reaction: libsbml.Reaction, kinetic_model: KineticModel, ) -> None: """ Adds kinetic law to SBML reaction. Only relevant for EnzymeML > SBML conversion. Args: reaction (libsbml.Reaction): SBML reaction class kinetic_model (KineticModel): PyEnzyme Model object """ # Set up SBML kinetic law node sbml_law = reaction.createKineticLaw() for parameter in kinetic_model.parameters: if parameter.is_global: continue param = self._write_local_parameter(parameter, sbml_law) # Create a mapping for the 'enzymeml:parameter' annotation annotation = self.setupXMLNode("enzymeml:parameter") optional_parameters = { "enzymeml:initialValue": "initial_value", "enzymeml:upperBound": "upper", "enzymeml:lowerBound": "lower", "enzymeml:stdev": "stdev", } for attributeName, objectName in optional_parameters.items(): self.appendOptionalAttribute( attributeName=attributeName, object=parameter, objectName=objectName, annotationNode=annotation, ) if annotation.getNumChildren() > 0: param.appendAnnotation(annotation) sbml_law.setMath(libsbml.parseL3Formula(kinetic_model.equation)) sbml_law.setName(kinetic_model.name) if kinetic_model.ontology: sbml_law.setSBOTerm(kinetic_model.ontology) def _write_local_parameter( self, parameter: KineticParameter, kinetic_law: libsbml.KineticLaw, ): """Writes a local parameter to a reaction""" local_param = kinetic_law.createLocalParameter() local_param.setId(parameter.name) if parameter.value is not None: local_param.setValue(parameter.value) if parameter._unit_id: local_param.setUnits(parameter._unit_id) if parameter.ontology: local_param.setSBOTerm(parameter.ontology) return local_param
[docs] def writeElements( self, reaction_elements: List[ReactionElement], createFunction: Callable ): """Writes SpeciesReference elements to the SBML document. Args: reaction_elements (List[ReactionElement]): List of reaction elements containing information on stoichiometry, species_id and ontology. createFunction (Callable): Function that creates either an educt, product or modifier list to the SBML document. """ for reaction_element in reaction_elements: speciesRef = createFunction() speciesRef.setSpecies(reaction_element.species_id) speciesRef.setSBOTerm(reaction_element.ontology) try: # Catch modifiers --> No stoich/constant in SBML speciesRef.setConstant(reaction_element.constant) speciesRef.setStoichiometry(reaction_element.stoichiometry) except AttributeError: pass
[docs] def writeReplicateData(self, replicate: Replicate, format_annot: XMLNode) -> None: """Writes column information to the enzymeml:format annotation. Args: replicate (Replicate): Replicate holding the time course data. format_annot (XMLNode): The enzymeml:format annotation node. """ column = self.setupXMLNode("enzymeml:column", namespace=False) # Add attributes column.addAttr("replica", replicate.id) column.addAttr("species", replicate.species_id) column.addAttr("type", replicate.data_type) column.addAttr("unit", replicate._data_unit_id) column.addAttr("index", str(self.index)) column.addAttr("isCalculated", str(replicate.is_calculated)) # Add colum to format annotation format_annot.addChild(column)
def _addData( self, model: libsbml.Model, measurement_dict: Dict[str, Measurement] ) -> Dict[str, str]: """Adds measurement data to the SBML document and writes time course data to DataFrames/CSV. Args: model (libsbml.Model): The SBML model to which the measurements are added. measurement_dict (Dict[str, Measurement]): The EnzymeMLDocument measurement data. Returns: Dict[str, str]: Mapping from actual CSV paths to the ones added to the OMEX """ # Initialize data lists data_annotation = self.setupXMLNode("enzymeml:data") files = self.setupXMLNode("enzymeml:files", namespace=False) formats = self.setupXMLNode("enzymeml:formats", namespace=False) measurements = self.setupXMLNode("enzymeml:listOfMeasurements", namespace=False) paths = {} for index, (measurement_id, measurement) in enumerate(measurement_dict.items()): # setup format/Measurement ID/node and file ID format_id = f"format{index}" file_id = f"file{index}" format_annot = self.setupXMLNode("enzymeml:format", namespace=False) format_annot.addAttr("id", format_id) measurement_annot = self.setupXMLNode( "enzymeml:measurement", namespace=False ) # Get time and add to format node data_columns = {} if measurement._has_replicates(): # Only write time data if replicates are present self.writeTimeData( measurement=measurement, format_annot=format_annot, data_columns=data_columns, ) self.writeMeasurementData( measurement=measurement, measurement_annot=measurement_annot, format_annot=format_annot, data_columns=data_columns, ) # Create DataFrame to save measurement if data_columns: file_name = f"{measurement_id}.csv" file_path = f"./data/{file_name}" df = pd.DataFrame(data_columns) if self.path: df_path = os.path.join(self.path, "data", file_name) df.to_csv(df_path, index=False, header=False) else: df_path = "Unused" paths[df_path] = file_path # Add data to data annotation file_annot = self.setupXMLNode("enzymeml:file", namespace=False) file_annot.addAttr("file", file_path) file_annot.addAttr("format", format_id) file_annot.addAttr("id", file_id) files.addChild(file_annot) formats.addChild(format_annot) measurement_annot.addAttr("file", file_id) measurement_annot.addAttr("id", measurement_id) measurement_annot.addAttr("name", measurement.name) # TODO find a sustainable way if measurement.temperature: measurement_annot.addAttr( "temperature_unit", measurement._temperature_unit_id ) measurement_annot.addAttr( "temperature_value", str(measurement.temperature) ) if measurement.ph: measurement_annot.addAttr("ph", str(measurement.ph)) measurements.addChild(measurement_annot) # add annotation to listOfReactions if formats.getNumChildren() > 0: data_annotation.addChild(formats) if measurements.getNumChildren() > 0: data_annotation.addChild(measurements) if files.getNumChildren() > 0: data_annotation.addChild(files) if data_annotation.getNumChildren() > 0: model.getListOfReactions().appendAnnotation(data_annotation) return paths
[docs] def writeTimeData( self, measurement: Measurement, format_annot: XMLNode, data_columns: dict ): time = measurement.global_time time_unit = measurement._global_time_unit_id time_column = XMLNode(XMLTriple("enzymeml:column"), XMLAttributes()) time_column.addAttr("type", "time") time_column.addAttr("unit", time_unit) time_column.addAttr("index", "0") format_annot.addChild(time_column) # write initConc annotation and prepare raw data data_columns.update({f"time/{time_unit}": time}) self.index = 1
[docs] def writeMeasurementData( self, measurement: Measurement, measurement_annot: XMLNode, format_annot: XMLNode, data_columns: Dict[str, List[float]], ) -> None: """Writes measurement metadata as columns to the SBML document annotation enzymeml:column. Args: measurement (Measurement): EnzymeML measurement that is written to SBML. measurement_annot (XMLNode): The SBML XMLNode the measurement metadata is written to. format_annot (XMLNode): The SBML XMLNode the column information is written to. Returns: List[List[float]]: The time course data from the replicate objects. """ species_dict = measurement.species_dict # Init Conc # Extract measurementData objects proteins = species_dict["proteins"] reactants = species_dict["reactants"] # Append initConc data to measurement self.appendInitConcData( measurement_annot=measurement_annot, measurement_data_dict=proteins, species_type="protein", ) self.appendInitConcData( measurement_annot=measurement_annot, measurement_data_dict=reactants, species_type="reactant", ) if measurement._has_replicates(): # Replicates self.appendReplicateData( {**proteins, **reactants}, format_annot=format_annot, data_columns=data_columns, )
[docs] def appendInitConcData( self, measurement_annot: XMLNode, measurement_data_dict: Dict[str, MeasurementData], species_type: str, ): """Adds individual intial concentration data to the enzymeml:measurement annotation. Args: measurement_annot (XMLNode): The SBML XMLNode for the measurement annotation. measurement_data_dict (Dict[str, MeasurementData]): The EnzymeMLDocument measurement data. species_type (str): The type of species in the measurement_data_dict. """ for species_id, measurement_data in measurement_data_dict.items(): # Create the initConc annotation initConcAnnot = self.setupXMLNode("enzymeml:initConc", namespace=False) initConcAnnot.addAttr(f"{species_type}", species_id) initConcAnnot.addAttr("value", str(measurement_data.init_conc)) # TODO Check if the unit given in the unit attribute is compliant # TODO Handle this in the base class by assigning a 'None' to the _unit_id unit_id_check = self.enzmldoc._convertToUnitDef(measurement_data.unit) if measurement_data._unit_id != unit_id_check: measurement_data._unit_id = unit_id_check initConcAnnot.addAttr("unit", measurement_data._unit_id) measurement_annot.addChild(initConcAnnot)
[docs] def appendReplicateData( self, species: Dict[str, MeasurementData], format_annot: XMLNode, data_columns: Dict[str, List[float]], ) -> Dict[str, List[float]]: """Extracts all time course data from the replicate objects and adds them to the the enzymeml:format annotation. Args: species (Dict[str, MeasurementData]): Reactant/Protein measurement data. format_annot (XMLNode): The SBML XMLNode representing the format annotation. Returns: List[List[float]]: The time course data from the replicate objects. """ # Collect all replicates replicates = [ replicate for data in species.values() for replicate in data.replicates ] for replicate in replicates: # Write data to format_annot self.writeReplicateData(replicate=replicate, format_annot=format_annot) # Extract series data header_info = "/".join( [replicate.id, replicate.species_id, replicate.data_type.value] ) data_columns[header_info] = replicate.data self.index += 1 return data_columns
def _validate_sbml_doc(self, document): """Validates an SBML documentument and checks for errors.""" document.checkConsistency() if document.getNumErrors(libsbml.LIBSBML_SEV_ERROR) > 0: warnings.warn("Invalid SBML document! See warnings:") warnings.warn(document.getErrorLog().toString())