sys-bio / libsbml-draw

Reads in an SBML file, and displays the network by generating a layout or using an existing layout. The SBML layout info can be saved to a new file.
GNU General Public License v3.0
3 stars 1 forks source link

Unable to save a model with layout information #15

Open CiaranWelsh opened 4 years ago

CiaranWelsh commented 4 years ago
import os
from libsbml_draw import SBMLlayout
from libsbml_draw.styles import Style, black_and_white
from libsbml_draw.utils import biomodels_download

model_id = 'BIOMD0000000001'

fname = model_id + '.xml'

# download the model
biomodels_download(model_id, fname)

s = SBMLlayout(fname)

s.drawNetwork(model_id + ".png")

# write the newly generated layout and rendering information back to the
# sbml model
s.writeSBMLFile(fname)

Fails with:

Traceback (most recent call last):
  File "D:\libsbml-draw\tests\test_working_with_biomodels.py", line 78, in test_save_sbml_to_file
    self.sl.writeSBMLFile(self.sbml_layout_fname)
  File "D:\libsbml-draw\src\python\libsbml_draw\sbml_layout.py", line 792, in writeSBMLFile
    This level {level} version {version} document has no layout information.""")  # noqa
ValueError: Cannot write file.  
            This level 2 version 1 document has no layout information.
CiaranWelsh commented 4 years ago

I've added a method to SBMLLayout to convert to level 3 version 1:


    def _convertToLatestSBML(self):
        """
        Convert `doc` to latest sbml version
        if it is not already
        Args:
            doc: sbml document

        Returns: sbml document

        """
        latestLevel = self._doc.getDefaultLevel()
        latestVersion = self._doc.getDefaultVersion()
        self._doc.setLevelAndVersion(3, 1)
        return self._doc

However I still get the same error.

Error
Traceback (most recent call last):
  File "C:\Miniconda3\envs\testing-libsbml-draw\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\Miniconda3\envs\testing-libsbml-draw\lib\unittest\case.py", line 628, in run
    testMethod()
  File "D:\libsbml-draw\tests\test_working_with_biomodels.py", line 78, in test_save_sbml_to_file
    self.sl.writeSBMLFile(self.sbml_layout_fname)
  File "D:\libsbml-draw\src\python\libsbml_draw\layout.py", line 825, in writeSBMLFile
    This level {level} version {version} document has no layout information.""")  # noqa
Exception: Cannot write file.
                This level 3 version 1 document has no layout information.

Note this is a custom error in writeSBMLFile which checks that the number of species glyphs is > 0.


    def writeSBMLFile(self, out_file_name):
        """Writes the model as an SBML file.

        Args:
            out_file_name (str): name of the file to write

        Returns: None
        """
        level = self._doc.getLevel()
        version = self._doc.getVersion()

        layout_number = self._layout_number
        layout_plugin = self._doc.getModel().getPlugin("layout")

        if not layout_plugin:
            raise ValueError('Could not find layout plugin')

        if layout_plugin.getNumLayouts() > 0:
            layout = layout_plugin.getLayout(layout_number)
        else:
            raise ValueError('No layouts in your sbml model')

        if layout.getNumSpeciesGlyphs() == 0:
            raise ValueError(f"""Cannot write file.
                This level {level} version {version} document has no layout information.""")  # noqa

        self._addRenderInformation()
        print(layout.getNumSpeciesGlyphs())

        libsbml.writeSBML(self._doc, out_file_name)
luciansmith commented 4 years ago

This implies that no layout information was ever added to the model. I don't know the functions, but your script implies that some should have been added either with the construction of the SBMLLayout(fname) line or (less likely) in the 'drawNetwork' function. It also implies that the .png was indeed created: can you look at it? Is it blank?

CiaranWelsh commented 4 years ago

Yes, I think your right in all accounts. The png is created and is what you would expect. The layout information is generated but apparently isn't being updated (perhaps). I've checked that lisbml can do this, which it can, so the problem is somewhere in libsbml-draw. Will keep digging.

luciansmith commented 4 years ago

One thing that you might find helpful is that libsbml will return error codes when it fails to do something, and the error code is usually a hint about what went wrong. I have seen (and written) a lot of code that doesn't check the codes at all, but here you might find it helpful. If (say) you have a 'model.addLayout(layoutobj)' line, you can change it from:

model.addLayout(obj)

to

code = model.addLayout(obj) assert(code == libsbml.LIBSBML_OPERATION_SUCCESS) #Or however you write this in Python; this is a guess.

CiaranWelsh commented 4 years ago

Update

The underlying C++ package has methods for writing to sbml but the libsbml-draw does not use them for writing, instead preferring libsbml directly. I suspect this is why we are having the issue with missing render/layout information when we save to file. Specifically, in sbnw these methods are between lines 2171 and 2210 in layout.h.

To use these other methods we need to extend the c api using ctypes. I have begun this process on the develop branch lines 148-180 and the corresponding test (begins on line 75).

The throws returns the following error:

Error
Traceback (most recent call last):
  File "C:\Miniconda3\envs\py37\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\Miniconda3\envs\py37\lib\unittest\case.py", line 628, in run
    testMethod()
  File "D:\libsbml-draw\tests\test_working_with_biomodels.py", line 87, in test_new_write_to_sbml
    self.sl.writeSBML(self.sbml_layout_fname)
  File "D:\libsbml-draw\src\python\libsbml_draw\layout.py", line 784, in writeSBML
    return sbnw.writeSBMLwithLayout(filename, self._h_model, self._h_layout_info)
  File "D:\libsbml-draw\src\python\libsbml_draw\sbnw.py", line 180, in writeSBMLwithLayout
    return slib.gf_writeSBMLwithLayout(filename, model, layout)
Exception: argument 1: <class 'TypeError'>: wrong type
-------------------- >> begin captured logging << --------------------
matplotlib: DEBUG: CACHEDIR=C:\Users\cwelsh\.matplotlib
matplotlib.font_manager: DEBUG: Using fontManager instance from C:\Users\cwelsh\.matplotlib\fontlist-v310.json
matplotlib.pyplot: DEBUG: Loaded backend module://backend_interagg version unknown.
urllib3.connectionpool: DEBUG: Starting new HTTPS connection (1): www.ebi.ac.uk:443
urllib3.connectionpool: DEBUG: https://www.ebi.ac.uk:443 "GET /biomodels-main/download?mid=BIOMD0000000091 HTTP/1.1" 200 None
--------------------- >> end captured logging << ---------------------

So there is a type mismatch somewhere, which sounds like it should be easy to fix. However, nothing I have tried has worked so far.