Skip to content

Commit af43a93

Browse files
committed
Updates to core sbml model definition
1 parent d65bc18 commit af43a93

File tree

5 files changed

+86
-12
lines changed

5 files changed

+86
-12
lines changed

docs/sphinx/source/api/Contributors.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Modelspec contributors
44

55
This page list names and Github profiles of contributors to Modelspec, listed in no particular order.
6-
This page is generated periodically, most recently on 2023-09-13.
6+
This page is generated periodically, most recently on 2023-09-18.
77

88
- Padraig Gleeson ([@pgleeson](https://github.com/pgleeson))
99
- Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin))

examples/sbml/example_sbml_minimal.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<sbml xmlns="http://www.sbml.org/sbml/level3/version1/core" level="3" version="1">
2+
<sbml xmlns="http://www.sbml.org/sbml/level3/version2/core" level="3" version="2">
33
<model substanceUnits="mole" timeUnits="second" extentUnits="mole">
44
<listOfUnitDefinitions>
55
<unitDefinition id="per_second">

examples/sbml/sbml32spec.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class SBase(Base):
6666

6767
sid: str = field(default=None, validator=optional([instance_of(str), valid_sid]))
6868
name: str = field(default=None, validator=optional(instance_of(str)))
69+
6970
metaid: str = field(
7071
default=None, validator=optional([instance_of(str), valid_xml_id])
7172
)
@@ -79,6 +80,12 @@ class SBase(Base):
7980
)
8081

8182

83+
@modelspec.define
84+
class SBaseWithId(SBase):
85+
86+
id: str = field(default=None, validator=optional([instance_of(str), valid_sid]))
87+
88+
8289
@modelspec.define
8390
class Trigger(SBase):
8491
initialValue: bool = field(default=None, validator=instance_of(bool))
@@ -345,7 +352,7 @@ class Unit(SBase):
345352

346353

347354
@modelspec.define
348-
class UnitDefinition(SBase):
355+
class unitDefinition(SBase):
349356
"""
350357
A unit definition
351358
@@ -412,7 +419,7 @@ class Model(SBase):
412419
)
413420

414421
listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list)
415-
listOfUnitDefinitions: List[UnitDefinition] = field(factory=list)
422+
listOfUnitDefinitions: List[unitDefinition] = field(factory=list)
416423
listOfCompartments: List[Compartment] = field(factory=list)
417424
listOfSpecies: List[Species] = field(factory=list)
418425
listOfParameters: List[Parameter] = field(factory=list)
@@ -424,7 +431,7 @@ class Model(SBase):
424431

425432

426433
@modelspec.define
427-
class SBML(SBase):
434+
class sbml(SBaseWithId):
428435
"""
429436
The top-level SBML container implementing SBML 3.2.
430437
See sbml.level-3.version-2.core.release-2.pdf section 4.
@@ -439,10 +446,11 @@ class SBML(SBase):
439446
"""
440447

441448
xmlns: str = field(
442-
default="http://www.sbml.org/sbml/level3/version2/core",
449+
default="http://www.sbml.org/sbml/level3/version%s/core" % SBML_VERSION,
443450
validator=[instance_of(str), xmlns_sbml],
444451
)
445-
level: str = field(default="3", validator=[instance_of(str), fixed_level])
446-
version: str = field(default="2", validator=[instance_of(str), fixed_version])
452+
level: str = field(default=None, validator=[instance_of(str), fixed_level])
453+
version: str = field(default=None, validator=[instance_of(str), fixed_version])
447454

448455
model: Model = field(default=None, validator=optional(instance_of(Model)))
456+
# models: List[Model] = field(factory=list)

examples/sbml/sbml_validators.py

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@
66
import re
77
from lxml import etree
88
from io import StringIO
9+
import sys
910

1011
# sbml.level-3.version-2.core.release-2.pdf Table 2
1112
sbml_si_units = """
1213
ampere coulomb gray joule litre mole radian steradian weber avogadro dimensionless henry katal lumen newton
1314
second tesla becquerel farad hertz kelvin lux ohm siemens volt candela gram item kilogram metre pascal sievert watt
1415
""".split()
1516

17+
SBML_VERSION = "2"
18+
SBML_LEVEL = "3"
19+
1620

1721
def valid_kind(instance, attribute, value):
1822
if not value in sbml_si_units:
@@ -43,13 +47,13 @@ def xmlns_math(instance, attribute, value):
4347

4448

4549
def fixed_level(instance, attribute, value):
46-
if value != "3":
47-
raise ValueError("this implementation only supports level 3")
50+
if value != SBML_LEVEL:
51+
raise ValueError("this implementation only supports level %s" % SBML_LEVEL)
4852

4953

5054
def fixed_version(instance, attribute, value):
51-
if value != "2":
52-
raise ValueError("this implementation only supports level 2")
55+
if value != SBML_VERSION:
56+
raise ValueError("this implementation only supports version %s" % SBML_VERSION)
5357

5458

5559
def valid_sid(instance, attribute, value):
@@ -114,3 +118,60 @@ def valid_mathml(instance, attribute, value):
114118
raise ValueError(
115119
f"{value} doesn't look like MathML (this validator is only a stub)"
116120
)
121+
122+
123+
# from: https://github.com/combine-org/combine-notebooks
124+
125+
126+
def validate_sbml(doc, units_consistency: bool = False) -> None:
127+
"""Validate sbml."""
128+
import libsbml
129+
130+
# set the unit checking, similar for the other settings
131+
doc.setConsistencyChecks(libsbml.LIBSBML_CAT_UNITS_CONSISTENCY, units_consistency)
132+
doc.checkConsistency()
133+
# get errors/warnings
134+
n_errors: int = doc.getNumErrors()
135+
errors: List[libsbml.SBMLError] = list()
136+
warnings: List[libsbml.SBMLError] = list()
137+
for k in range(n_errors):
138+
error: libsbml.SBMLError = doc.getError(k)
139+
severity = error.getSeverity()
140+
if (severity == libsbml.LIBSBML_SEV_ERROR) or (
141+
severity == libsbml.LIBSBML_SEV_FATAL
142+
):
143+
errors.append(error)
144+
else:
145+
warnings.append(error)
146+
# print results
147+
print("-" * 80)
148+
print(f"{'validation error(s)':<25}: {len(errors)}")
149+
print(f"{'validation warning(s)':<25}: {len(warnings)}")
150+
if len(errors) > 0:
151+
print("--- errors ---")
152+
for kerr in enumerate(errors):
153+
print(f"E{kerr}: {error.getCategoryAsString()} L{error.getLine()}")
154+
print(
155+
f"[{error.getSeverityAsString()}] {error.getShortMessage()} | {error.getMessage()}"
156+
)
157+
if len(warnings) > 0:
158+
print("--- warnings ---")
159+
for kwarn in enumerate(warnings):
160+
print(f"E{kwarn}: {error.getCategoryAsString()} L{error.getLine()}")
161+
print(
162+
f"[{error.getSeverityAsString()}] {error.getShortMessage()} | {error.getMessage()}"
163+
)
164+
print("-" * 80)
165+
166+
167+
if __name__ == "__main__":
168+
169+
import libsbml
170+
171+
sbml_file = sys.argv[1]
172+
173+
print("Going to validate SBML file: %s" % sbml_file)
174+
reader = libsbml.SBMLReader()
175+
document = reader.readSBML(sbml_file)
176+
177+
validate_sbml(document, units_consistency=False)

test_all.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ python neuroml2_spec.py
2424
pynml -validate hello_world_neuroml.net.nml
2525
pynml -validate TestNeuroML.xml
2626

27+
## Test SBML example
28+
29+
cd ../sbml
30+
./regenerateAndTest.sh
31+
2732
cd ../..
2833

2934

0 commit comments

Comments
 (0)