Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fbc-v3: UserDefinedConstraintComponents not written in SBML L3V2 #257

Closed
matthiaskoenig opened this issue Jul 19, 2022 · 0 comments · Fixed by #264
Closed

fbc-v3: UserDefinedConstraintComponents not written in SBML L3V2 #257

matthiaskoenig opened this issue Jul 19, 2022 · 0 comments · Fixed by #264

Comments

@matthiaskoenig
Copy link

The UserDefinedConstraintComponents are not written in SBML L3V2.

Only the empty component is written in SBML, but not the attributes:

    <fbc:listOfUserDefinedConstraints>
      <fbc:userDefinedConstraint>
        <fbc:listOfUserDefinedConstraintComponents>
          <fbc:userDefinedConstraintComponent/>
        </fbc:listOfUserDefinedConstraintComponents>
      </fbc:userDefinedConstraint>
    </fbc:listOfUserDefinedConstraints>

See full example below:

"""
Test bed for FBC version 3.
For latest SBML fbc v3 specification see
https://github.com/bgoli/sbml-fbc-spec/blob/main/sf_svn/spec/main.pdf
"""
from logging import getLogger
from pathlib import Path

import libsbml

logger = getLogger(__name__)


def check(value: int, message: str) -> bool:
    """Check the libsbml return value and prints message if something happened.

    If 'value' is None, prints an error message constructed using
      'message' and then exits with status code 1. If 'value' is an integer,
      it assumes it is a libSBML return status code. If the code value is
      LIBSBML_OPERATION_SUCCESS, returns without further action; if it is not,
      prints an error message constructed using 'message' along with text from
      libSBML explaining the meaning of the code, and exits with status code 1.
    """
    valid = True
    if value is None:
        logger.error(f"Error: LibSBML returned a null value trying to <{message}>.")
        valid = False
    elif isinstance(value, int):
        if value != libsbml.LIBSBML_OPERATION_SUCCESS:
            logger.error(f"Error encountered trying to '{message}'.")
            logger.error(
                f"LibSBML returned error code {str(value)}: "
                f"{libsbml.OperationReturnValue_toString(value).strip()}"
            )
            valid = False

    return valid


# create document with new fbc version 3 namespace
sbmlns: libsbml.SBMLNamespaces = libsbml.SBMLNamespaces(3, 2)
sbmlns.addPkgNamespace("fbc", 3)

doc: libsbml.SBMLDocument = libsbml.SBMLDocument(sbmlns)
doc_fbc: libsbml.FbcSBMLDocumentPlugin = doc.getPlugin("fbc")
doc_fbc.setRequired(False)

model: libsbml.Model = doc.createModel()
model_fbc: libsbml.FbcModelPlugin = model.getPlugin("fbc")
model_fbc.setStrict(True)

# Support for key value pairs exists
c: libsbml.Compartment = model.createCompartment()
c.setId("c1")
c.setSize(1.0)
c.setConstant(True)

s1: libsbml.Species = model.createSpecies()
s1.setId("s1")
s1.setCompartment("c1")
s1.setInitialConcentration(1.0)
s1.setConstant(False)
s1.setHasOnlySubstanceUnits(False)
s1.setBoundaryCondition(False)

s2: libsbml.Species = model.createSpecies()
s2.setId("s2")
s2.setCompartment("c1")
s2.setInitialConcentration(1.0)
s2.setConstant(False)
s2.setHasOnlySubstanceUnits(False)
s2.setBoundaryCondition(False)

# Support exists for user constraints
reaction: libsbml.Reaction = model.createReaction()
reaction.setId("r1")
reaction.setReversible(True)
reaction.setFast(False)
fbc_reaction: libsbml.FbcReactionPlugin = reaction.getPlugin("fbc")
fbc_reaction.setUpperFluxBound("ub")
fbc_reaction.setLowerFluxBound("lb")

reactant: libsbml.SpeciesReference = reaction.createReactant()
reactant.setSpecies("s1")
reactant.setConstant(True)
reactant.setStoichiometry(1.0)

product: libsbml.SpeciesReference = reaction.createProduct()
product.setSpecies("s2")
product.setConstant(True)
product.setStoichiometry(1.0)


constraint: libsbml.UserDefinedConstraint = model_fbc.createUserDefinedConstraint()
constraint.setId("constraint1")
constraint.setLowerBound("lb")
constraint.setUpperBound("ub")

component: libsbml.UserDefinedConstraintComponent = (
    constraint.createUserDefinedConstraintComponent()
)
component.setCoefficient(10.0)
component.setVariable("r1")
component.setVariableType(libsbml.FBC_FBCVARIABLETYPE_LINEAR)

sbml_str: str = libsbml.writeSBMLToString(doc)
print("-" * 80)
# console.log(sbml_str)
print(sbml_str)
print("-" * 80)


# checking that SBML string can be read again
doc2 = libsbml.readSBMLFromString(sbml_str)
if doc2.getNumErrors() > 0:
    if doc2.getError(0).getErrorId() == libsbml.XMLFileUnreadable:
        err_message = "Unreadable SBML file"
    elif doc2.getError(0).getErrorId() == libsbml.XMLFileOperationError:
        err_message = "Problems reading SBML file: XMLFileOperationError"
    else:
        err_message = "SBMLDocumentErrors encountered while reading the SBML file."

    # log_sbml_errors_for_doc(doc)
    logger.error(f"`read_sbml` error `{err_message}`")
    for k in range(doc2.getNumErrors()):
        error: libsbml.SBMLError = doc2.getError(k)
        print(f"Error {k}")
        print(error.getMessage())

Results in

<?xml version="1.0" encoding="UTF-8"?>
<sbml xmlns="http://www.sbml.org/sbml/level3/version2/core" xmlns:fbc="http://www.sbml.org/sbml/level3/version1/fbc/version3" level="3" version="2" fbc:required="false">
  <model fbc:strict="true">
    <listOfCompartments>
      <compartment id="c1" size="1" constant="true"/>
    </listOfCompartments>
    <listOfSpecies>
      <species id="s1" compartment="c1" initialConcentration="1" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false">
        <annotation>
          <listOfKeyValuePairs xmlns="http://sbml.org/fbc/keyvaluepair">
            <keyValuePair key="testdata" value="1.0"/>
          </listOfKeyValuePairs>
        </annotation>
      </species>
      <species id="s2" compartment="c1" initialConcentration="1" hasOnlySubstanceUnits="false" boundaryCondition="false" constant="false"/>
    </listOfSpecies>
    <listOfReactions>
      <reaction id="r1" reversible="true" fbc:lowerFluxBound="lb" fbc:upperFluxBound="ub">
        <listOfReactants>
          <speciesReference species="s1" stoichiometry="1" constant="true"/>
        </listOfReactants>
        <listOfProducts>
          <speciesReference species="s2" stoichiometry="1" constant="true"/>
        </listOfProducts>
      </reaction>
    </listOfReactions>
    <fbc:listOfUserDefinedConstraints>
      <fbc:userDefinedConstraint>
        <fbc:listOfUserDefinedConstraintComponents>
          <fbc:userDefinedConstraintComponent/>
        </fbc:listOfUserDefinedConstraintComponents>
      </fbc:userDefinedConstraint>
    </fbc:listOfUserDefinedConstraints>
  </model>
</sbml>

If you change the line to

sbmlns: libsbml.SBMLNamespaces = libsbml.SBMLNamespaces(3, 1)

the component is serialized correctly as

    <fbc:listOfUserDefinedConstraints>
      <fbc:userDefinedConstraint fbc:id="constraint1" fbc:lowerBound="lb" fbc:upperBound="ub">
        <fbc:listOfUserDefinedConstraintComponents>
          <fbc:userDefinedConstraintComponent fbc:coefficient="10" fbc:variable="r1" fbc:variableType="linear"/>
        </fbc:listOfUserDefinedConstraintComponents>
      </fbc:userDefinedConstraint>
    </fbc:listOfUserDefinedConstraints>
This was referenced Aug 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant