Source code for sbmlutils.manipulation.merge

"""Merging of SBML models.

The following is a helper function for merging multiple SBML models into
a single model.
"""
import os
from pathlib import Path
from typing import Dict, Optional

import libsbml

from sbmlutils import log
from sbmlutils.comp import comp, flatten_sbml
from sbmlutils.io import read_sbml, validate_sbml, write_sbml
from sbmlutils.validation import ValidationOptions


[docs]logger = log.get_logger(__name__)
[docs]def merge_models( model_paths: Dict[str, Path], output_dir: Path, merged_id: str = "merged", flatten: bool = True, validate: bool = True, validate_input: bool = True, validation_options: Optional[ValidationOptions] = None, sbml_level: int = 3, sbml_version: int = 1, ) -> libsbml.SBMLDocument: """Merge SBML models. Merges SBML models given in `model_paths` in the `output_dir`. Models are provided as dictionary { 'model1_id': model1_path, 'model2_id': model2_path, ... } The model ids are used as ids for the ExternalModelDefinitions. Relative paths are set in the merged models. The created model is either in SBML L3V1 (default) or SBML L3V2. :param model_paths: absolute paths to models :param output_dir: output directory for merged model :param merged_id: model id of the merged model :param flatten: flattens the merged model :param validate: boolean flag to validate the merged model :param validate_input: boolean flag to validate the input models :param validation_options: ValidationOptions :param sbml_level: SBML Level of the merged model in [3] :param sbml_version: SBML Version of the merged model in [1, 2] :return: SBMLDocument of the merged models """ # necessary to convert models to SBML L3V1 if isinstance(output_dir, str): logger.warning(f"'output_dir' should be a Path but: '{type(output_dir)}'") output_dir = Path(output_dir) if not output_dir.exists(): raise IOError(f"'output_dir' does not exist: {output_dir}") for model_id, path in model_paths.items(): if not path.exists(): raise IOError(f"Path for SBML file does not exist: {path}") if isinstance(path, str): path = Path(path) # convert to L3V1 path_L3: Path = output_dir / f"{model_id}_L3.xml" doc = read_sbml(path) doc.setLevelAndVersion(sbml_level, sbml_version) write_sbml(doc, path_L3) model_paths[model_id] = path_L3 if validate_input: validate_sbml( source=path_L3, title=str(path), validation_options=validation_options, ) # create comp model cur_dir = os.getcwd() os.chdir(str(output_dir)) merged_doc: libsbml.SBMLDocument = _create_merged_doc( model_paths, merged_id=merged_id ) os.chdir(cur_dir) # write merged doc merged_path = output_dir / f"{merged_id}.xml" write_sbml(merged_doc, filepath=merged_path) if validate: validate_sbml( source=merged_path, validation_options=validation_options, title=str(merged_path), ) if flatten: flat_path = output_dir / f"{merged_id}_flat.xml" flatten_sbml(sbml_path=merged_path, sbml_flat_path=flat_path) if validate: validate_sbml( source=flat_path, validation_options=validation_options, title=str(flat_path), ) return merged_doc
[docs]def _create_merged_doc( model_paths: Dict[str, Path], merged_id: str = "merged", sbml_level: int = 3, sbml_version: int = 1, ) -> libsbml.SBMLDocument: """Create a comp model from given model paths. Warning: This only works if all models are in the same directory. """ sbmlns = libsbml.SBMLNamespaces(sbml_level, sbml_version) sbmlns.addPackageNamespace("comp", 1) doc: libsbml.SBMLDocument = libsbml.SBMLDocument(sbmlns) doc.setPackageRequired("comp", True) model: libsbml.Model = doc.createModel() model.setId(merged_id) comp_doc: libsbml.CompSBMLDocumentPlugin = doc.getPlugin("comp") comp_model: libsbml.CompModelPlugin = model.getPlugin("comp") for emd_id, path in model_paths.items(): # create ExternalModelDefinition emd: libsbml.ExternalModelDefinition = comp.create_ExternalModelDefinition( comp_doc, emd_id, source=str(path) ) # add submodel which references the external model definition comp.add_submodel_from_emd(comp_model, submodel_id=emd_id, emd=emd) return doc