From d3c302e1ee2b1ed3c05aab8533341fa5d240b792 Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Thu, 12 Jan 2023 10:15:20 +0000
Subject: [PATCH 01/32] Install hdf5 from source
---
.github/workflows/ci.yml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8b770161..e3683cc7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,6 +35,7 @@ jobs:
- name: Install package
run: |
+ if [[ ${{ matrix.python-version }} == 3.11 ]]; then pip3 install --no-binary=h5py h5py ; fi
python -m pip install --upgrade pip
pip install .[dev]
@@ -56,7 +57,7 @@ jobs:
- name: Run pytest
run: |
- pytest tests
+ pytest tests -v
- name: Install & test NeuroMLlite
run: |
From f49bb8862ee9c242f9d0b0ebbad14e1c4464f286 Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Thu, 12 Jan 2023 10:20:34 +0000
Subject: [PATCH 02/32] install libhdf5-dev
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e3683cc7..1c0e3a0f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
- name: Install package
run: |
- if [[ ${{ matrix.python-version }} == 3.11 ]]; then pip3 install --no-binary=h5py h5py ; fi
+ if [[ ${{ matrix.python-version }} == 3.11 ]]; then apt install -y libhdf5-dev; pip3 install --no-binary=h5py h5py ; fi
python -m pip install --upgrade pip
pip install .[dev]
From ad4df1922bf80bd1cdba33361079dc7915819dee Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Thu, 12 Jan 2023 10:28:55 +0000
Subject: [PATCH 03/32] sudo
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1c0e3a0f..8d7fea41 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
- name: Install package
run: |
- if [[ ${{ matrix.python-version }} == 3.11 ]]; then apt install -y libhdf5-dev; pip3 install --no-binary=h5py h5py ; fi
+ if [[ ${{ matrix.python-version }} == 3.11 ]]; then sudo apt install -y libhdf5-dev; pip3 install --no-binary=h5py h5py ; fi
python -m pip install --upgrade pip
pip install .[dev]
From f25194c61c89fb46646d5d80340a19a5bb226afb Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Tue, 31 Jan 2023 16:42:07 +0000
Subject: [PATCH 04/32] Update ci.yml
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8d7fea41..050a41a1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
- name: Install package
run: |
- if [[ ${{ matrix.python-version }} == 3.11 ]]; then sudo apt install -y libhdf5-dev; pip3 install --no-binary=h5py h5py ; fi
+ if [[ ${{ matrix.python-version }} == 3.11 ]]; then sudo apt-get install libhdf5-serial-dev liblzo2-dev -y; pip3 install --no-binary=h5py h5py ; fi
python -m pip install --upgrade pip
pip install .[dev]
From 9770a5a82848be618ddfc1386d40cdf3199d2930 Mon Sep 17 00:00:00 2001
From: "Ankur Sinha (Ankur Sinha Gmail)"
Date: Wed, 26 Jul 2023 12:01:38 +0100
Subject: [PATCH 05/32] ci(py311): add to matrix
---
.github/workflows/ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bb406efa..92b331bc 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: [ "3.7", "3.8", "3.9", "3.10"]
+ python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11"]
runs-on: [ubuntu-latest, macos-latest, windows-latest]
steps:
From 2b9235110b83211792dfbd8f77d94d9c27faecc4 Mon Sep 17 00:00:00 2001
From: Robert Vickerstaff
Date: Sat, 29 Jul 2023 11:59:38 +0000
Subject: [PATCH 06/32] added simple sbml test with example xml showing desired
output we should be aiming to produce
---
examples/sbml/example_sbml_minimal.xml | 12 +
examples/sbml/sbml32spec.py | 379 ++++++++++++++++++++++++
examples/sbml/sbml_validators.py | 88 ++++++
examples/sbml/test_minimal_example.json | 18 ++
examples/sbml/test_sbml3.py | 36 +++
5 files changed, 533 insertions(+)
create mode 100644 examples/sbml/example_sbml_minimal.xml
create mode 100644 examples/sbml/sbml32spec.py
create mode 100644 examples/sbml/sbml_validators.py
create mode 100644 examples/sbml/test_minimal_example.json
create mode 100755 examples/sbml/test_sbml3.py
diff --git a/examples/sbml/example_sbml_minimal.xml b/examples/sbml/example_sbml_minimal.xml
new file mode 100644
index 00000000..fe9b1524
--- /dev/null
+++ b/examples/sbml/example_sbml_minimal.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/sbml/sbml32spec.py b/examples/sbml/sbml32spec.py
new file mode 100644
index 00000000..400cff3f
--- /dev/null
+++ b/examples/sbml/sbml32spec.py
@@ -0,0 +1,379 @@
+#!/usr/bin/env python3
+
+'''
+initial attempt at creating an SBML API using modelspec
+https://github.com/combine-org/compbiolibs/issues/28
+
+based on sbml.level-3.version-2.core.release-2.pdf
+'''
+
+import modelspec
+from modelspec import field, instance_of, optional
+from modelspec.base_types import Base
+from typing import List
+
+from sbml_validators import *
+
+@modelspec.define
+class Notes(Base):
+ '''
+ XHTML field of SBase
+
+ Args:
+ xmlns: str fixed "http://www.w3.org/1999/xhtml"
+ content: str valid XHTML
+ '''
+ xmlns: str = field(default="http://www.w3.org/1999/xhtml",validator=[instance_of(str),xmlns_notes])
+ content: str = field(default=None,validator=optional([instance_of(str),valid_xhtml]))
+
+@modelspec.define
+class Math(Base):
+ '''
+ Subset of MathML 2.0 used to define all formulae in SBML
+ '''
+ xmlns: str = field(default="http://www.w3.org/1998/Math/MathML",validator=[instance_of(str),xmlns_math])
+ content: str = field(default=None,validator=optional([instance_of(str),valid_mathml]))
+
+@modelspec.define
+class SBase(Base):
+ """
+ Abstract base class for all SBML objects
+
+ Args:
+ sid: SId optional
+ name: string optional
+ metaid: XML ID optional
+ sboTerm: SBOTerm optional
+
+ notes: XHTML 1.0 optional
+ annotation: XML content optional
+ """
+
+ sid: str = field(default=None,validator=optional([instance_of(str),valid_sid]))
+ name: str = field(default=None,validator=optional(instance_of(str)))
+ metaid: str = field(default=None,validator=optional([instance_of(str),valid_xml_id]))
+ sboTerm: str = field(default=None,validator=optional([instance_of(str),valid_sbo]))
+
+ notes: Notes = field(default=None,validator=optional(instance_of(Notes)))
+ annotation: str = field(default=None,validator=optional([instance_of(str),valid_xml_content]))
+
+@modelspec.define
+class Trigger(SBase):
+ initialValue: bool = field(default=None,validator=instance_of(bool))
+ persistent: bool = field(default=None,validator=instance_of(bool))
+ math: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class Priority(SBase):
+ math: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class Delay(SBase):
+ math: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class EventAssignment(SBase):
+ '''
+ Args:
+ variable: SIdRef
+ '''
+ math: str = field(default=None,validator=optional(instance_of(str)))
+ variable: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class Event(SBase):
+ useValuesFromTriggerTime: bool = field(default=None,validator=instance_of(bool))
+ trigger: Trigger = field(default=None, validator=optional(instance_of(Trigger)))
+ priority: Priority = field(default=None, validator=optional(instance_of(Priority)))
+ delay: Delay = field(default=None, validator=optional(instance_of(Delay)))
+ listOfEventAssignments: List[EventAssignment] = field(factory=list)
+
+@modelspec.define
+class SimpleSpeciesReference(SBase):
+ """
+ Base class used by SpeciesReference and ModifierSpeciesReference
+
+ Args:
+ species: SIdRef
+ """
+
+ species: str = field(default=None,validator=instance_of(str))
+
+@modelspec.define
+class ModifierSpeciesReference(SimpleSpeciesReference):
+ ''
+
+@modelspec.define
+class SpeciesReference(SimpleSpeciesReference):
+ """
+ Args:
+ stoichiometry: double optional
+ constant: boolean
+ """
+
+ stoichiometry: float = field(default=None,validator=optional(instance_of(float)))
+ constant: bool = field(default=None,validator=instance_of(bool))
+
+@modelspec.define
+class LocalParameter(SBase):
+ """
+ Args:
+ units: UnitSIdRef optional
+ """
+
+ value: float = field(default=None,validator=optional(instance_of(float)))
+ units: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class KineticLaw(SBase):
+ """
+ """
+
+ math: str = field(default=None,validator=optional(instance_of(str)))
+
+ listOfLocalParameters: List[LocalParameter] = field(factory=list)
+
+@modelspec.define
+class Reaction(SBase):
+ """
+ A model reaction
+
+ Args:
+ reversible: boolean
+ compartment: SIdRef optional
+ """
+
+ reversible: bool = field(default=None,validator=instance_of(bool))
+ compartment: str = field(default=None,validator=optional(instance_of(str)))
+
+ listOfReactants: List[SpeciesReference] = field(factory=list)
+ listOfProducts: List[SpeciesReference] = field(factory=list)
+ listOfModifiers: List[ModifierSpeciesReference] = field(factory=list)
+
+ kineticLaw: KineticLaw = field(default=None, validator=optional(instance_of(KineticLaw)))
+
+@modelspec.define
+class Constraint(SBase):
+ """
+ A model constraint
+
+ Args:
+ math: MathML optional
+ message: XHTML 1.0 optional
+ """
+
+ math: str = field(default=None,validator=optional(instance_of(str)))
+ message: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class Rule(SBase):
+ """
+ A rule, either algebraic, assignment or rate
+
+ Args:
+ math: MathML optional
+ """
+
+ math: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class AlgebraicRule(Rule):
+ """
+ An algebraic rule
+ """
+
+@modelspec.define
+class AssignmentRule(Rule):
+ """
+ An assignment rule
+
+ Args:
+ variable: SIdRef required
+ """
+
+ variable: str = field(default=None,validator=instance_of(str))
+
+@modelspec.define
+class RateRule(Rule):
+ """
+ A rate rule
+
+ Args:
+ variable: SIdRef required
+ """
+
+ variable: str = field(default=None,validator=instance_of(str))
+
+@modelspec.define
+class InitialAssignment(SBase):
+ """
+ An initial assignment
+
+ Args:
+ symbol: SIdRef required
+ math: MathML optional
+ """
+
+ symbol: str = field(default=None,validator=instance_of(str))
+ math: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class Parameter(SBase):
+ """
+ A parameter
+
+ Args:
+ value: double optional
+ units: UnitSIdRef optional
+ constant: boolean
+ """
+
+ constant: bool = field(default=None,validator=instance_of(bool))
+
+ value: float = field(default=None,validator=optional(instance_of(float)))
+ units: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class Species(SBase):
+ """
+ A species: entities of the same kind participating in reactions within a specific compartment
+
+ Args:
+ compartment: SIdRef
+ initialAmount: double optional
+ initialConcentration: double optional
+ substanceUnits: UnitSIdRef optional
+ hasOnlySubstanceUnits: boolean
+ boundaryCondition: boolean
+ constant: boolean
+ conversionFactor: SIdRef optional
+ """
+
+ compartment: str = field(default=None,validator=instance_of(str))
+ hasOnlySubstanceUnits: bool = field(default=None,validator=instance_of(bool))
+ boundaryCondition: bool = field(default=None,validator=instance_of(bool))
+ constant: bool = field(default=None,validator=instance_of(bool))
+
+ initialAmount: float = field(default=None, validator=optional(instance_of(float)))
+ initialConcentration: float = field(default=None, validator=optional(instance_of(float)))
+ substanceUnits: str = field(default=None, validator=optional(instance_of(str)))
+ conversionFactor: str = field(default=None, validator=optional(instance_of(str)))
+
+@modelspec.define
+class Compartment(SBase):
+ """
+ A compartment
+
+ Args:
+ spatialDimensions: eg 3 for three dimensional space etc
+ size: initial size of compartment
+ units: units being used to define the compartment's size
+ constant: whether size is fixed
+ """
+
+ constant: bool = field(default=None,validator=instance_of(bool))
+
+ spatialDimensions: float = field(default=None,validator=optional(instance_of(float)))
+ size: float = field(default=None,validator=optional(instance_of(float)))
+ units: str = field(default=None,validator=optional(instance_of(str)))
+
+@modelspec.define
+class Unit(SBase):
+ """
+ A unit used to compose a unit definition.
+ unit = (multiplier x 10^scale x kind)^exponent
+
+ Args:
+ kind: base unit (base or derived SI units only, see Table 2 of the SBML spec)
+ exponent: double
+ scale: integer
+ multiplier: double
+ """
+
+ kind: str = field(default=None,validator=[instance_of(str),valid_kind])
+ exponent: str = field(default=1.0, validator=instance_of(float))
+ scale: str = field(default=0, validator=instance_of(int))
+ multiplier: str = field(default=1.0, validator=instance_of(float))
+
+@modelspec.define
+class UnitDefinition(SBase):
+ """
+ A unit definition
+
+ Args:
+ sid: UnitSid required (overrides SBase sid)
+ listOfUnits: List of units used to compose the definition
+ """
+
+ sid: str = field(default=None,validator=[instance_of(str),valid_unitsid])
+ listOfUnits: List[Unit] = field(factory=list)
+
+@modelspec.define
+class FunctionDefinition(SBase):
+ """
+ A function definition using MathML
+
+ Args:
+ sid: SId required
+
+ math: MathML function definition optional
+ """
+
+ sid: str = field(default=None,validator=[instance_of(str),valid_sid])
+
+ math: Math = field(default=None, validator=optional(instance_of(Math)))
+
+@modelspec.define
+class Model(SBase):
+ """
+ The model
+
+ Args:
+ substanceUnits: UnitSIdRef optional
+ timeUnits: UnitSIdRef optional
+ volumeUnits: UnitSIdRef optional
+ areaUnits: UnitSIdRef optional
+ lengthUnits: UnitSIdRef optional
+ extentUnits: UnitSIdRef optional
+ conversionFactor: SIdRef optional
+ """
+
+ substanceUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
+ timeUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
+ volumeUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
+ areaUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
+ lengthUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
+ extentUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
+ conversionFactor: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
+
+ listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list)
+ listOfUnitDefinitions: List[UnitDefinition] = field(factory=list)
+ listOfCompartments: List[Compartment] = field(factory=list)
+ listOfSpecies: List[Species] = field(factory=list)
+ listOfParameters: List[Parameter] = field(factory=list)
+ listOfInitialAssignments: List[InitialAssignment] = field(factory=list)
+ listOfRules: List[Rule] = field(factory=list)
+ listOfConstraints: List[Constraint] = field(factory=list)
+ listOfReactions: List[Reaction] = field(factory=list)
+ listOfEvents: List[Event] = field(factory=list)
+
+@modelspec.define
+class SBML(SBase):
+ """
+ The top-level SBML container implementing SBML 3.2.
+ See sbml.level-3.version-2.core.release-2.pdf section 4.
+ http://www.sbml.org/sbml/level3/version2/core
+
+ Args:
+ xmlns: string, fixed to "http://www.sbml.org/sbml/level3/version2/core"
+ level: SBML level, fixed to 3
+ version: SBML version, fixed to 2
+
+ model: Optional model
+ """
+
+ xmlns: str = field(default="http://www.sbml.org/sbml/level3/version2/core",validator=[instance_of(str),xmlns_sbml])
+ level: str = field(default="3",validator=[instance_of(str),fixed_level])
+ version: str = field(default="2",validator=[instance_of(str),fixed_version])
+
+ model: Model = field(default=None, validator=optional(instance_of(Model)))
diff --git a/examples/sbml/sbml_validators.py b/examples/sbml/sbml_validators.py
new file mode 100644
index 00000000..008c49c3
--- /dev/null
+++ b/examples/sbml/sbml_validators.py
@@ -0,0 +1,88 @@
+'''
+functions used to validate the user assigned values of items
+these functions will generally be called by passing to the validator option of attrs.field
+'''
+
+import re
+from lxml import etree
+from io import StringIO
+
+#sbml.level-3.version-2.core.release-2.pdf Table 2
+sbml_si_units=\
+'''
+ampere coulomb gray joule litre mole radian steradian weber avogadro dimensionless henry katal lumen newton
+second tesla becquerel farad hertz kelvin lux ohm siemens volt candela gram item kilogram metre pascal sievert watt
+'''.split()
+
+def valid_kind(instance, attribute, value):
+ if not value in sbml_si_units:
+ raise ValueError(f"kind {value} must be one of the standard SI units: {sbml_si_units}")
+
+def valid_mathml(instance, attribute, value):
+ 'http://www.w3.org/1998/Math/MathML'
+
+def xmlns_sbml(instance, attribute, value):
+ if value != "http://www.sbml.org/sbml/level3/version2/core":
+ raise ValueError("xmlns must be 'http://www.sbml.org/sbml/level3/version2/core'")
+
+def xmlns_notes(instance, attribute, value):
+ if value != "http://www.w3.org/1999/xhtml":
+ raise ValueError("xmlns must be 'http://www.w3.org/1999/xhtml'")
+
+def xmlns_math(instance, attribute, value):
+ if value != "http://www.w3.org/1998/Math/MathML":
+ raise ValueError("xmlns must be 'http://www.w3.org/1998/Math/MathML'")
+
+def fixed_level(instance, attribute, value):
+ if value != "3":
+ raise ValueError("this implementation only supports level 3")
+
+def fixed_version(instance, attribute, value):
+ if value != "2":
+ raise ValueError("this implementation only supports level 2")
+
+def valid_sid(instance, attribute, value):
+ if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value):
+ raise ValueError("an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*")
+
+def valid_unitsid(instance, attribute, value):
+ 'same as sid except has a separate namespace'
+ if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value):
+ raise ValueError("a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*")
+
+def valid_unitsid(instance, attribute, value):
+ if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value):
+ raise ValueError("a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*")
+
+def valid_sbo(instance, attribute, value):
+ if not re.fullmatch('SBO:[0-9]{7}',value):
+ raise ValueError("an SBOTerm must match the regular expression: SBO:[0-9]{7}")
+
+def valid_xml_id(instance,attribute,value):
+ 'a valid XML 1.0 ID'
+ #not implemented yet: CombiningChar , Extender
+ #NameChar ::= letter | digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
+ #ID ::= ( letter | '_' | ':' ) NameChar*
+
+ if not re.fullmatch('[A-Za-z_:][A-Za-z0-9._:-]*',value):
+ raise ValueError("an SBOTerm must match the regular expression: SBO:[0-9]{7}")
+
+def valid_xhtml(instance,attribute,value):
+ 'use etree to validate XHTML, throw exception on error'
+ etree.parse(StringIO(value), etree.HTMLParser(recover=False))
+
+def valid_xml_content(instance,attribute,value):
+ 'stub'
+
+ if not re.search('<.*>',value):
+ raise ValueError(f"{value} doesn't look like XML (this validator is only a stub)")
+
+def valid_mathml(instance,attribute,value):
+ '''
+ stubvalidator for MathML
+ note: see pdf section 4.3.2 for special rules for FunctionDefinition MathML
+ versus all other MathML uses in SBML
+ '''
+
+ if not re.search('',value):
+ raise ValueError(f"{value} doesn't look like MathML (this validator is only a stub)")
diff --git a/examples/sbml/test_minimal_example.json b/examples/sbml/test_minimal_example.json
new file mode 100644
index 00000000..e8419a1f
--- /dev/null
+++ b/examples/sbml/test_minimal_example.json
@@ -0,0 +1,18 @@
+{
+ "model": {
+ "substanceUnits": "mole",
+ "timeUnits": "second",
+ "extentUnits": "mole",
+ "listOfUnitDefinitions": [
+ {
+ "sid": "per_second",
+ "listOfUnits": [
+ {
+ "kind": "second",
+ "exponent": -1.0
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py
new file mode 100755
index 00000000..d7c59338
--- /dev/null
+++ b/examples/sbml/test_sbml3.py
@@ -0,0 +1,36 @@
+#!/usr/bin/env python3
+
+'''
+derived from https://github.com/combine-org/draft-modelspec-sbml-api/blob/main/test_sbml32.py
+a minimalish sbml to xml example
+'''
+
+import json
+import yaml
+import os
+
+from sbml32spec import *
+
+def test_example_sbml_minimal():
+ 'aiming to match the xml file example_sbml_minimal.xml'
+
+ path = "test_minimal_example"
+
+ sbml_doc = SBML()
+ #open(f"{path}.docs.json", "w").write(json.dumps(sbml_doc.generate_documentation(format="dict"), indent=4))
+
+ model = Model(substanceUnits="mole",timeUnits="second",extentUnits="mole")
+ sbml_doc.model = model
+
+ unit_def = UnitDefinition(sid="per_second")
+ model.listOfUnitDefinitions.append(unit_def)
+
+ unit = Unit(kind="second",exponent=-1.0)
+ unit_def.listOfUnits.append(unit)
+
+ sbml_doc.to_json_file(f"{path}.json")
+ sbml_doc.to_xml_file(f"{path}.xml")
+
+
+if __name__ == "__main__":
+ test_example_sbml_minimal()
From f0f8e6cd92d1163b046fb2a3476d49b16ef1bb9e Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Mon, 31 Jul 2023 15:47:06 +0100
Subject: [PATCH 07/32] Formatted new sbml files
@robertvi It's good to run the test script at:
https://github.com/ModECI/modelspec/blob/development/test_all.sh before
committing as that will run the precommit hooks, including formatting
with black. Tests will fail if the formatting is not done. See
https://mdf.readthedocs.io/en/latest/api/Contributing.html#step-7
---
examples/sbml/sbml32spec.py | 245 +++++++++++++++---------
examples/sbml/sbml_validators.py | 98 ++++++----
examples/sbml/test_minimal_example.json | 2 +-
examples/sbml/test_sbml3.py | 15 +-
4 files changed, 229 insertions(+), 131 deletions(-)
diff --git a/examples/sbml/sbml32spec.py b/examples/sbml/sbml32spec.py
index 400cff3f..997607e0 100644
--- a/examples/sbml/sbml32spec.py
+++ b/examples/sbml/sbml32spec.py
@@ -1,11 +1,11 @@
#!/usr/bin/env python3
-'''
+"""
initial attempt at creating an SBML API using modelspec
https://github.com/combine-org/compbiolibs/issues/28
based on sbml.level-3.version-2.core.release-2.pdf
-'''
+"""
import modelspec
from modelspec import field, instance_of, optional
@@ -14,25 +14,40 @@
from sbml_validators import *
+
@modelspec.define
class Notes(Base):
- '''
+ """
XHTML field of SBase
Args:
xmlns: str fixed "http://www.w3.org/1999/xhtml"
content: str valid XHTML
- '''
- xmlns: str = field(default="http://www.w3.org/1999/xhtml",validator=[instance_of(str),xmlns_notes])
- content: str = field(default=None,validator=optional([instance_of(str),valid_xhtml]))
+ """
+
+ xmlns: str = field(
+ default="http://www.w3.org/1999/xhtml",
+ validator=[instance_of(str), xmlns_notes],
+ )
+ content: str = field(
+ default=None, validator=optional([instance_of(str), valid_xhtml])
+ )
+
@modelspec.define
class Math(Base):
- '''
+ """
Subset of MathML 2.0 used to define all formulae in SBML
- '''
- xmlns: str = field(default="http://www.w3.org/1998/Math/MathML",validator=[instance_of(str),xmlns_math])
- content: str = field(default=None,validator=optional([instance_of(str),valid_mathml]))
+ """
+
+ xmlns: str = field(
+ default="http://www.w3.org/1998/Math/MathML",
+ validator=[instance_of(str), xmlns_math],
+ )
+ content: str = field(
+ default=None, validator=optional([instance_of(str), valid_mathml])
+ )
+
@modelspec.define
class SBase(Base):
@@ -49,44 +64,57 @@ class SBase(Base):
annotation: XML content optional
"""
- sid: str = field(default=None,validator=optional([instance_of(str),valid_sid]))
- name: str = field(default=None,validator=optional(instance_of(str)))
- metaid: str = field(default=None,validator=optional([instance_of(str),valid_xml_id]))
- sboTerm: str = field(default=None,validator=optional([instance_of(str),valid_sbo]))
+ sid: str = field(default=None, validator=optional([instance_of(str), valid_sid]))
+ name: str = field(default=None, validator=optional(instance_of(str)))
+ metaid: str = field(
+ default=None, validator=optional([instance_of(str), valid_xml_id])
+ )
+ sboTerm: str = field(
+ default=None, validator=optional([instance_of(str), valid_sbo])
+ )
+
+ notes: Notes = field(default=None, validator=optional(instance_of(Notes)))
+ annotation: str = field(
+ default=None, validator=optional([instance_of(str), valid_xml_content])
+ )
- notes: Notes = field(default=None,validator=optional(instance_of(Notes)))
- annotation: str = field(default=None,validator=optional([instance_of(str),valid_xml_content]))
@modelspec.define
class Trigger(SBase):
- initialValue: bool = field(default=None,validator=instance_of(bool))
- persistent: bool = field(default=None,validator=instance_of(bool))
- math: str = field(default=None,validator=optional(instance_of(str)))
+ initialValue: bool = field(default=None, validator=instance_of(bool))
+ persistent: bool = field(default=None, validator=instance_of(bool))
+ math: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class Priority(SBase):
- math: str = field(default=None,validator=optional(instance_of(str)))
+ math: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class Delay(SBase):
- math: str = field(default=None,validator=optional(instance_of(str)))
+ math: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class EventAssignment(SBase):
- '''
+ """
Args:
variable: SIdRef
- '''
- math: str = field(default=None,validator=optional(instance_of(str)))
- variable: str = field(default=None,validator=optional(instance_of(str)))
+ """
+
+ math: str = field(default=None, validator=optional(instance_of(str)))
+ variable: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class Event(SBase):
- useValuesFromTriggerTime: bool = field(default=None,validator=instance_of(bool))
- trigger: Trigger = field(default=None, validator=optional(instance_of(Trigger)))
+ useValuesFromTriggerTime: bool = field(default=None, validator=instance_of(bool))
+ trigger: Trigger = field(default=None, validator=optional(instance_of(Trigger)))
priority: Priority = field(default=None, validator=optional(instance_of(Priority)))
- delay: Delay = field(default=None, validator=optional(instance_of(Delay)))
- listOfEventAssignments: List[EventAssignment] = field(factory=list)
+ delay: Delay = field(default=None, validator=optional(instance_of(Delay)))
+ listOfEventAssignments: List[EventAssignment] = field(factory=list)
+
@modelspec.define
class SimpleSpeciesReference(SBase):
@@ -97,11 +125,13 @@ class SimpleSpeciesReference(SBase):
species: SIdRef
"""
- species: str = field(default=None,validator=instance_of(str))
+ species: str = field(default=None, validator=instance_of(str))
+
@modelspec.define
class ModifierSpeciesReference(SimpleSpeciesReference):
- ''
+ """"""
+
@modelspec.define
class SpeciesReference(SimpleSpeciesReference):
@@ -111,8 +141,9 @@ class SpeciesReference(SimpleSpeciesReference):
constant: boolean
"""
- stoichiometry: float = field(default=None,validator=optional(instance_of(float)))
- constant: bool = field(default=None,validator=instance_of(bool))
+ stoichiometry: float = field(default=None, validator=optional(instance_of(float)))
+ constant: bool = field(default=None, validator=instance_of(bool))
+
@modelspec.define
class LocalParameter(SBase):
@@ -121,17 +152,18 @@ class LocalParameter(SBase):
units: UnitSIdRef optional
"""
- value: float = field(default=None,validator=optional(instance_of(float)))
- units: str = field(default=None,validator=optional(instance_of(str)))
+ value: float = field(default=None, validator=optional(instance_of(float)))
+ units: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class KineticLaw(SBase):
- """
- """
+ """ """
+
+ math: str = field(default=None, validator=optional(instance_of(str)))
- math: str = field(default=None,validator=optional(instance_of(str)))
+ listOfLocalParameters: List[LocalParameter] = field(factory=list)
- listOfLocalParameters: List[LocalParameter] = field(factory=list)
@modelspec.define
class Reaction(SBase):
@@ -143,14 +175,17 @@ class Reaction(SBase):
compartment: SIdRef optional
"""
- reversible: bool = field(default=None,validator=instance_of(bool))
- compartment: str = field(default=None,validator=optional(instance_of(str)))
+ reversible: bool = field(default=None, validator=instance_of(bool))
+ compartment: str = field(default=None, validator=optional(instance_of(str)))
- listOfReactants: List[SpeciesReference] = field(factory=list)
- listOfProducts: List[SpeciesReference] = field(factory=list)
- listOfModifiers: List[ModifierSpeciesReference] = field(factory=list)
+ listOfReactants: List[SpeciesReference] = field(factory=list)
+ listOfProducts: List[SpeciesReference] = field(factory=list)
+ listOfModifiers: List[ModifierSpeciesReference] = field(factory=list)
+
+ kineticLaw: KineticLaw = field(
+ default=None, validator=optional(instance_of(KineticLaw))
+ )
- kineticLaw: KineticLaw = field(default=None, validator=optional(instance_of(KineticLaw)))
@modelspec.define
class Constraint(SBase):
@@ -162,8 +197,9 @@ class Constraint(SBase):
message: XHTML 1.0 optional
"""
- math: str = field(default=None,validator=optional(instance_of(str)))
- message: str = field(default=None,validator=optional(instance_of(str)))
+ math: str = field(default=None, validator=optional(instance_of(str)))
+ message: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class Rule(SBase):
@@ -174,7 +210,8 @@ class Rule(SBase):
math: MathML optional
"""
- math: str = field(default=None,validator=optional(instance_of(str)))
+ math: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class AlgebraicRule(Rule):
@@ -182,6 +219,7 @@ class AlgebraicRule(Rule):
An algebraic rule
"""
+
@modelspec.define
class AssignmentRule(Rule):
"""
@@ -191,7 +229,8 @@ class AssignmentRule(Rule):
variable: SIdRef required
"""
- variable: str = field(default=None,validator=instance_of(str))
+ variable: str = field(default=None, validator=instance_of(str))
+
@modelspec.define
class RateRule(Rule):
@@ -202,7 +241,8 @@ class RateRule(Rule):
variable: SIdRef required
"""
- variable: str = field(default=None,validator=instance_of(str))
+ variable: str = field(default=None, validator=instance_of(str))
+
@modelspec.define
class InitialAssignment(SBase):
@@ -214,8 +254,9 @@ class InitialAssignment(SBase):
math: MathML optional
"""
- symbol: str = field(default=None,validator=instance_of(str))
- math: str = field(default=None,validator=optional(instance_of(str)))
+ symbol: str = field(default=None, validator=instance_of(str))
+ math: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class Parameter(SBase):
@@ -228,10 +269,11 @@ class Parameter(SBase):
constant: boolean
"""
- constant: bool = field(default=None,validator=instance_of(bool))
+ constant: bool = field(default=None, validator=instance_of(bool))
+
+ value: float = field(default=None, validator=optional(instance_of(float)))
+ units: str = field(default=None, validator=optional(instance_of(str)))
- value: float = field(default=None,validator=optional(instance_of(float)))
- units: str = field(default=None,validator=optional(instance_of(str)))
@modelspec.define
class Species(SBase):
@@ -249,16 +291,19 @@ class Species(SBase):
conversionFactor: SIdRef optional
"""
- compartment: str = field(default=None,validator=instance_of(str))
- hasOnlySubstanceUnits: bool = field(default=None,validator=instance_of(bool))
- boundaryCondition: bool = field(default=None,validator=instance_of(bool))
- constant: bool = field(default=None,validator=instance_of(bool))
+ compartment: str = field(default=None, validator=instance_of(str))
+ hasOnlySubstanceUnits: bool = field(default=None, validator=instance_of(bool))
+ boundaryCondition: bool = field(default=None, validator=instance_of(bool))
+ constant: bool = field(default=None, validator=instance_of(bool))
initialAmount: float = field(default=None, validator=optional(instance_of(float)))
- initialConcentration: float = field(default=None, validator=optional(instance_of(float)))
+ initialConcentration: float = field(
+ default=None, validator=optional(instance_of(float))
+ )
substanceUnits: str = field(default=None, validator=optional(instance_of(str)))
conversionFactor: str = field(default=None, validator=optional(instance_of(str)))
+
@modelspec.define
class Compartment(SBase):
"""
@@ -271,11 +316,14 @@ class Compartment(SBase):
constant: whether size is fixed
"""
- constant: bool = field(default=None,validator=instance_of(bool))
+ constant: bool = field(default=None, validator=instance_of(bool))
+
+ spatialDimensions: float = field(
+ default=None, validator=optional(instance_of(float))
+ )
+ size: float = field(default=None, validator=optional(instance_of(float)))
+ units: str = field(default=None, validator=optional(instance_of(str)))
- spatialDimensions: float = field(default=None,validator=optional(instance_of(float)))
- size: float = field(default=None,validator=optional(instance_of(float)))
- units: str = field(default=None,validator=optional(instance_of(str)))
@modelspec.define
class Unit(SBase):
@@ -290,11 +338,12 @@ class Unit(SBase):
multiplier: double
"""
- kind: str = field(default=None,validator=[instance_of(str),valid_kind])
- exponent: str = field(default=1.0, validator=instance_of(float))
- scale: str = field(default=0, validator=instance_of(int))
+ kind: str = field(default=None, validator=[instance_of(str), valid_kind])
+ exponent: str = field(default=1.0, validator=instance_of(float))
+ scale: str = field(default=0, validator=instance_of(int))
multiplier: str = field(default=1.0, validator=instance_of(float))
+
@modelspec.define
class UnitDefinition(SBase):
"""
@@ -305,9 +354,10 @@ class UnitDefinition(SBase):
listOfUnits: List of units used to compose the definition
"""
- sid: str = field(default=None,validator=[instance_of(str),valid_unitsid])
+ sid: str = field(default=None, validator=[instance_of(str), valid_unitsid])
listOfUnits: List[Unit] = field(factory=list)
+
@modelspec.define
class FunctionDefinition(SBase):
"""
@@ -319,10 +369,11 @@ class FunctionDefinition(SBase):
math: MathML function definition optional
"""
- sid: str = field(default=None,validator=[instance_of(str),valid_sid])
+ sid: str = field(default=None, validator=[instance_of(str), valid_sid])
math: Math = field(default=None, validator=optional(instance_of(Math)))
+
@modelspec.define
class Model(SBase):
"""
@@ -338,24 +389,39 @@ class Model(SBase):
conversionFactor: SIdRef optional
"""
- substanceUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
- timeUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
- volumeUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
- areaUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
- lengthUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
- extentUnits: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
- conversionFactor: str = field(default=None, validator=optional([instance_of(str),valid_unitsid]))
+ substanceUnits: str = field(
+ default=None, validator=optional([instance_of(str), valid_unitsid])
+ )
+ timeUnits: str = field(
+ default=None, validator=optional([instance_of(str), valid_unitsid])
+ )
+ volumeUnits: str = field(
+ default=None, validator=optional([instance_of(str), valid_unitsid])
+ )
+ areaUnits: str = field(
+ default=None, validator=optional([instance_of(str), valid_unitsid])
+ )
+ lengthUnits: str = field(
+ default=None, validator=optional([instance_of(str), valid_unitsid])
+ )
+ extentUnits: str = field(
+ default=None, validator=optional([instance_of(str), valid_unitsid])
+ )
+ conversionFactor: str = field(
+ default=None, validator=optional([instance_of(str), valid_unitsid])
+ )
listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list)
- listOfUnitDefinitions: List[UnitDefinition] = field(factory=list)
- listOfCompartments: List[Compartment] = field(factory=list)
- listOfSpecies: List[Species] = field(factory=list)
- listOfParameters: List[Parameter] = field(factory=list)
- listOfInitialAssignments: List[InitialAssignment] = field(factory=list)
- listOfRules: List[Rule] = field(factory=list)
- listOfConstraints: List[Constraint] = field(factory=list)
- listOfReactions: List[Reaction] = field(factory=list)
- listOfEvents: List[Event] = field(factory=list)
+ listOfUnitDefinitions: List[UnitDefinition] = field(factory=list)
+ listOfCompartments: List[Compartment] = field(factory=list)
+ listOfSpecies: List[Species] = field(factory=list)
+ listOfParameters: List[Parameter] = field(factory=list)
+ listOfInitialAssignments: List[InitialAssignment] = field(factory=list)
+ listOfRules: List[Rule] = field(factory=list)
+ listOfConstraints: List[Constraint] = field(factory=list)
+ listOfReactions: List[Reaction] = field(factory=list)
+ listOfEvents: List[Event] = field(factory=list)
+
@modelspec.define
class SBML(SBase):
@@ -372,8 +438,11 @@ class SBML(SBase):
model: Optional model
"""
- xmlns: str = field(default="http://www.sbml.org/sbml/level3/version2/core",validator=[instance_of(str),xmlns_sbml])
- level: str = field(default="3",validator=[instance_of(str),fixed_level])
- version: str = field(default="2",validator=[instance_of(str),fixed_version])
+ xmlns: str = field(
+ default="http://www.sbml.org/sbml/level3/version2/core",
+ validator=[instance_of(str), xmlns_sbml],
+ )
+ level: str = field(default="3", validator=[instance_of(str), fixed_level])
+ version: str = field(default="2", validator=[instance_of(str), fixed_version])
model: Model = field(default=None, validator=optional(instance_of(Model)))
diff --git a/examples/sbml/sbml_validators.py b/examples/sbml/sbml_validators.py
index 008c49c3..73894926 100644
--- a/examples/sbml/sbml_validators.py
+++ b/examples/sbml/sbml_validators.py
@@ -1,88 +1,116 @@
-'''
+"""
functions used to validate the user assigned values of items
these functions will generally be called by passing to the validator option of attrs.field
-'''
+"""
import re
from lxml import etree
from io import StringIO
-#sbml.level-3.version-2.core.release-2.pdf Table 2
-sbml_si_units=\
-'''
+# sbml.level-3.version-2.core.release-2.pdf Table 2
+sbml_si_units = """
ampere coulomb gray joule litre mole radian steradian weber avogadro dimensionless henry katal lumen newton
second tesla becquerel farad hertz kelvin lux ohm siemens volt candela gram item kilogram metre pascal sievert watt
-'''.split()
+""".split()
+
def valid_kind(instance, attribute, value):
if not value in sbml_si_units:
- raise ValueError(f"kind {value} must be one of the standard SI units: {sbml_si_units}")
+ raise ValueError(
+ f"kind {value} must be one of the standard SI units: {sbml_si_units}"
+ )
+
def valid_mathml(instance, attribute, value):
- 'http://www.w3.org/1998/Math/MathML'
+ "http://www.w3.org/1998/Math/MathML"
+
def xmlns_sbml(instance, attribute, value):
if value != "http://www.sbml.org/sbml/level3/version2/core":
- raise ValueError("xmlns must be 'http://www.sbml.org/sbml/level3/version2/core'")
+ raise ValueError(
+ "xmlns must be 'http://www.sbml.org/sbml/level3/version2/core'"
+ )
+
def xmlns_notes(instance, attribute, value):
if value != "http://www.w3.org/1999/xhtml":
raise ValueError("xmlns must be 'http://www.w3.org/1999/xhtml'")
+
def xmlns_math(instance, attribute, value):
if value != "http://www.w3.org/1998/Math/MathML":
raise ValueError("xmlns must be 'http://www.w3.org/1998/Math/MathML'")
+
def fixed_level(instance, attribute, value):
if value != "3":
raise ValueError("this implementation only supports level 3")
+
def fixed_version(instance, attribute, value):
if value != "2":
raise ValueError("this implementation only supports level 2")
+
def valid_sid(instance, attribute, value):
- if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value):
- raise ValueError("an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*")
+ if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value):
+ raise ValueError(
+ "an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*"
+ )
+
def valid_unitsid(instance, attribute, value):
- 'same as sid except has a separate namespace'
- if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value):
- raise ValueError("a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*")
+ "same as sid except has a separate namespace"
+ if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value):
+ raise ValueError(
+ "a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*"
+ )
+
def valid_unitsid(instance, attribute, value):
- if not re.fullmatch('[_A-Za-z][_A-Za-z0-9]*',value):
- raise ValueError("a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*")
+ if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value):
+ raise ValueError(
+ "a UnitSId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*"
+ )
+
def valid_sbo(instance, attribute, value):
- if not re.fullmatch('SBO:[0-9]{7}',value):
+ if not re.fullmatch("SBO:[0-9]{7}", value):
raise ValueError("an SBOTerm must match the regular expression: SBO:[0-9]{7}")
-def valid_xml_id(instance,attribute,value):
- 'a valid XML 1.0 ID'
- #not implemented yet: CombiningChar , Extender
- #NameChar ::= letter | digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
- #ID ::= ( letter | '_' | ':' ) NameChar*
- if not re.fullmatch('[A-Za-z_:][A-Za-z0-9._:-]*',value):
+def valid_xml_id(instance, attribute, value):
+ "a valid XML 1.0 ID"
+ # not implemented yet: CombiningChar , Extender
+ # NameChar ::= letter | digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
+ # ID ::= ( letter | '_' | ':' ) NameChar*
+
+ if not re.fullmatch("[A-Za-z_:][A-Za-z0-9._:-]*", value):
raise ValueError("an SBOTerm must match the regular expression: SBO:[0-9]{7}")
-
-def valid_xhtml(instance,attribute,value):
- 'use etree to validate XHTML, throw exception on error'
+
+
+def valid_xhtml(instance, attribute, value):
+ "use etree to validate XHTML, throw exception on error"
etree.parse(StringIO(value), etree.HTMLParser(recover=False))
-def valid_xml_content(instance,attribute,value):
- 'stub'
- if not re.search('<.*>',value):
- raise ValueError(f"{value} doesn't look like XML (this validator is only a stub)")
+def valid_xml_content(instance, attribute, value):
+ "stub"
+
+ if not re.search("<.*>", value):
+ raise ValueError(
+ f"{value} doesn't look like XML (this validator is only a stub)"
+ )
-def valid_mathml(instance,attribute,value):
- '''
+
+def valid_mathml(instance, attribute, value):
+ """
stubvalidator for MathML
note: see pdf section 4.3.2 for special rules for FunctionDefinition MathML
versus all other MathML uses in SBML
- '''
+ """
- if not re.search('',value):
- raise ValueError(f"{value} doesn't look like MathML (this validator is only a stub)")
+ if not re.search("", value):
+ raise ValueError(
+ f"{value} doesn't look like MathML (this validator is only a stub)"
+ )
diff --git a/examples/sbml/test_minimal_example.json b/examples/sbml/test_minimal_example.json
index e8419a1f..27e2a1f9 100644
--- a/examples/sbml/test_minimal_example.json
+++ b/examples/sbml/test_minimal_example.json
@@ -15,4 +15,4 @@
}
]
}
-}
\ No newline at end of file
+}
diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py
index d7c59338..1ee6dc18 100755
--- a/examples/sbml/test_sbml3.py
+++ b/examples/sbml/test_sbml3.py
@@ -1,9 +1,9 @@
#!/usr/bin/env python3
-'''
+"""
derived from https://github.com/combine-org/draft-modelspec-sbml-api/blob/main/test_sbml32.py
a minimalish sbml to xml example
-'''
+"""
import json
import yaml
@@ -11,21 +11,22 @@
from sbml32spec import *
+
def test_example_sbml_minimal():
- 'aiming to match the xml file example_sbml_minimal.xml'
+ "aiming to match the xml file example_sbml_minimal.xml"
path = "test_minimal_example"
sbml_doc = SBML()
- #open(f"{path}.docs.json", "w").write(json.dumps(sbml_doc.generate_documentation(format="dict"), indent=4))
-
- model = Model(substanceUnits="mole",timeUnits="second",extentUnits="mole")
+ # open(f"{path}.docs.json", "w").write(json.dumps(sbml_doc.generate_documentation(format="dict"), indent=4))
+
+ model = Model(substanceUnits="mole", timeUnits="second", extentUnits="mole")
sbml_doc.model = model
unit_def = UnitDefinition(sid="per_second")
model.listOfUnitDefinitions.append(unit_def)
- unit = Unit(kind="second",exponent=-1.0)
+ unit = Unit(kind="second", exponent=-1.0)
unit_def.listOfUnits.append(unit)
sbml_doc.to_json_file(f"{path}.json")
From 49d9fc6e1f70826b503c8be7e1515ca5203dc157 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Tue, 8 Aug 2023 13:15:17 +0100
Subject: [PATCH 08/32] Test 3.11
---
.github/workflows/ci.yml | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 69208b06..5b8706b3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- python-version: [ "3.7", "3.8", "3.9", "3.10"]
+ python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" ]
runs-on: [ubuntu-latest, macos-latest, windows-latest]
steps:
@@ -34,9 +34,16 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
+
+ - name: Install h5py
+ if: ${{ matrix.python-version == '3.11' }}
+ run: |
+ #if [[ ${{ matrix.runs-on }} == *"macos"* ]]; then brew install graphviz ; fi
+ #if [[ ${{ matrix.runs-on }} == *"ubuntu"* ]]; then sudo apt-get install libhdf5-serial-dev liblzo2-dev -y; pip3 install --no-binary=h5py h5py ; fi
+ pip list
+
- name: Install package
run: |
- if [[ ${{ matrix.python-version }} == 3.11 ]]; then sudo apt-get install libhdf5-serial-dev liblzo2-dev -y; pip3 install --no-binary=h5py h5py ; fi
python -m pip install --upgrade pip
pip install .[dev]
From c6e70f8e598a3e98b85e1bdd8d39c31589b4c11e Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Wed, 6 Sep 2023 14:07:21 +0100
Subject: [PATCH 09/32] Add comment
---
docs/sphinx/source/api/Contributors.md | 2 +-
test_all.sh | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md
index 66101321..3dfc0108 100644
--- a/docs/sphinx/source/api/Contributors.md
+++ b/docs/sphinx/source/api/Contributors.md
@@ -3,7 +3,7 @@
# Modelspec contributors
This page list names and Github profiles of contributors to Modelspec, listed in no particular order.
-This page is generated periodically, most recently on 2023-08-23.
+This page is generated periodically, most recently on 2023-09-06.
- Padraig Gleeson ([@pgleeson](https://github.com/pgleeson))
- Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin))
diff --git a/test_all.sh b/test_all.sh
index 0a91b46b..0834fcd2 100755
--- a/test_all.sh
+++ b/test_all.sh
@@ -33,6 +33,7 @@ pytest tests -v
## Run OMV tests
+# See https://github.com/OpenSourceBrain/osb-model-validation
omv all -V
From 9d3ffa911b1b1ac643526c467b9245c307785148 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Wed, 13 Sep 2023 10:31:18 +0100
Subject: [PATCH 10/32] Update gha version
---
.github/workflows/static.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml
index e146eb91..5720078f 100644
--- a/.github/workflows/static.yml
+++ b/.github/workflows/static.yml
@@ -33,7 +33,7 @@ jobs:
uses: actions/checkout@v3
- name: Set up Python
- uses: actions/setup-python@v2
+ uses: actions/setup-python@v4
with:
python-version: 3.9
From d03ffb2b55f1e6902432475f8d2b5a25aacda9c6 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Wed, 13 Sep 2023 10:32:46 +0100
Subject: [PATCH 11/32] Bump version to v0.3.3
---
src/modelspec/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/modelspec/__init__.py b/src/modelspec/__init__.py
index 8f33cf5a..b8bd118b 100644
--- a/src/modelspec/__init__.py
+++ b/src/modelspec/__init__.py
@@ -1,4 +1,4 @@
-__version__ = "0.3.2"
+__version__ = "0.3.3"
from .base_types import Base, define, has, field, fields, optional, instance_of, in_
From 2964ff40ad21256f91bda830f6a6e3836e51d485 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Wed, 13 Sep 2023 10:49:49 +0100
Subject: [PATCH 12/32] Some doc updates
---
README.md | 4 +---
docs/sphinx/source/api/Introduction.md | 4 +---
docs/sphinx/source/api/Quickstart.md | 11 ++++++-----
3 files changed, 8 insertions(+), 11 deletions(-)
diff --git a/README.md b/README.md
index 12721e5a..91de3fd5 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,8 @@
# Modelspec
-
[![Continuous builds](https://github.com/ModECI/modelspec/actions/workflows/ci.yml/badge.svg)](https://github.com/ModECI/modelspec/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/modelspec)](https://pypi.org/project/modelspec/)
-
-Functionality for specifying the structure of models & enabling automatic serialization to them (e.g. in JSON and YAML format).
+Functionality for specifying the allowed structure of models, facilitating the creation of APIs in Python for handling the models & enabling automatic serialization of instances of them (e.g. in JSON and YAML format).
This package is being used by [NeuroMLlite](https://github.com/NeuroML/NeuroMLlite) & [MDF](https://github.com/ModECI/MDF).
diff --git a/docs/sphinx/source/api/Introduction.md b/docs/sphinx/source/api/Introduction.md
index 12721e5a..91de3fd5 100644
--- a/docs/sphinx/source/api/Introduction.md
+++ b/docs/sphinx/source/api/Introduction.md
@@ -1,10 +1,8 @@
# Modelspec
-
[![Continuous builds](https://github.com/ModECI/modelspec/actions/workflows/ci.yml/badge.svg)](https://github.com/ModECI/modelspec/actions/workflows/ci.yml) [![PyPI](https://img.shields.io/pypi/v/modelspec)](https://pypi.org/project/modelspec/)
-
-Functionality for specifying the structure of models & enabling automatic serialization to them (e.g. in JSON and YAML format).
+Functionality for specifying the allowed structure of models, facilitating the creation of APIs in Python for handling the models & enabling automatic serialization of instances of them (e.g. in JSON and YAML format).
This package is being used by [NeuroMLlite](https://github.com/NeuroML/NeuroMLlite) & [MDF](https://github.com/ModECI/MDF).
diff --git a/docs/sphinx/source/api/Quickstart.md b/docs/sphinx/source/api/Quickstart.md
index 832b149a..b6adbf02 100644
--- a/docs/sphinx/source/api/Quickstart.md
+++ b/docs/sphinx/source/api/Quickstart.md
@@ -15,18 +15,19 @@ More details, and importantly, how to set up a [virtual environment](https://vir
### Simple example
-A basic example which illustrates how to create the specification for a document(model) and create serialized instances can be found [here](examples/README).
+A basic example which illustrates how to create the specification for a document (i.e. the model definition for a document) and create serialized instances can be found [here](examples/README).
### Serialization formats
-Python scripts can be used to generate the specification of a type of model(e.g. [this](https://github.com/ModECI/modelspec/blob/main/examples/document.py)), but the models are saved in standardized format in either text based [JSON](https://github.com/ModECI/modelspec/blob/main/examples/document.json) or [YAML](https://github.com/ModECI/modelspec/blob/main/examples/document.yaml) formats or in binary [BSON](https://github.com/ModECI/modelspec/blob/main/examples/document.bson) format. Support for XML serialization is under development.
+Python scripts can be used to generate the specification of a type of model (e.g. [this](https://github.com/ModECI/modelspec/blob/main/examples/document.py)), but the models are saved in standardized format in either text based ([JSON](https://github.com/ModECI/modelspec/blob/main/examples/document.json) or [YAML](https://github.com/ModECI/modelspec/blob/main/examples/document.yaml)) formats or in binary ([BSON](https://github.com/ModECI/modelspec/blob/main/examples/document.bson)) format. Support for [XML](https://github.com/ModECI/modelspec/blob/main/examples/document.xml) serialization has recently been added.
### Current formats using modelspec
-#### MDF
+#### MDF (Model Description Format)
-MDF uses modelspec to create the structure of its models and convert the models into serialized formats such as JSON, YAML, and BSON.
+[ModECI's MDF](https://modeci.org/quickstart) uses modelspec to create the structure of its models and convert the models into serialized formats such as JSON, YAML, and BSON.
#### NeuroML
-NeuroMLlite uses modelspec to create the structure of its models and convert the models into serialize formats such as JSON, YAML, and BSON.
+[NeuroMLlite](https://github.com/NeuroML/NeuroMLlite) uses modelspec to create the structure of its models and convert the models into serialize formats such as JSON, YAML, and BSON.
+The XML serialisation of modelspec will be useful for integrating NeuroML 2 files into the framework, see [here](https://github.com/ModECI/modelspec/tree/main/examples/neuroml2).
From 18ea90bcf48cf67689c90b682bc288dd88d1654f Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Mon, 18 Sep 2023 13:02:44 +0100
Subject: [PATCH 13/32] Update testing on save/load json/yaml
---
examples/test/test.md | 35 ++++++++++++++++++++++++
examples/test/test.py | 38 +++++++++++++++++++++++++--
examples/test/test.specification.yaml | 15 +++++++++++
examples/test/test_instance.json | 7 +++++
examples/test/test_instance.xml | 2 ++
examples/test/test_instance.yaml | 5 ++--
src/modelspec/base_types.py | 15 +++++++++++
tests/test_base.py | 20 ++++++++++++--
8 files changed, 131 insertions(+), 6 deletions(-)
create mode 100644 examples/test/test_instance.json
create mode 100644 examples/test/test_instance.xml
diff --git a/examples/test/test.md b/examples/test/test.md
index 366f1b45..f81c64cd 100644
--- a/examples/test/test.md
+++ b/examples/test/test.md
@@ -24,4 +24,39 @@ A model....
+
+ float_like_optional2 |
+ int |
+ name also says it all... |
+
+
+
+
+ mid |
+ MidClassNoId |
+ |
+
+
+
+
+
+## MidClassNoId
+A model....
+
+### Allowed parameters
+
+
+ int_val |
+ int |
+ name says it all... |
+
+
+
+
+ str_val |
+ int |
+ name says it all... |
+
+
+
diff --git a/examples/test/test.py b/examples/test/test.py
index f14d8165..b52e23c1 100644
--- a/examples/test/test.py
+++ b/examples/test/test.py
@@ -1,6 +1,7 @@
import modelspec
from modelspec import field, instance_of, optional
from modelspec.base_types import Base
+from modelspec.utils import load_json
from typing import List
# Example testing multiple options...
@@ -17,6 +18,20 @@ def convert2float(x: Any) -> float:
return None
+@modelspec.define
+class MidClassNoId(Base):
+ """
+ A model....
+
+ Args:
+ int_val: name says it all...
+ str_val: name says it all...
+ """
+
+ int_val: int = field(default=None, validator=instance_of(int))
+ str_val: int = field(default=None, validator=optional(instance_of(str)))
+
+
@modelspec.define
class TopClass(Base):
"""
@@ -26,6 +41,7 @@ class TopClass(Base):
id: The unique id of the thing
float_like_req: name says it all...
float_like_optional: name also says it all...
+ float_like_optional2: name also says it all...
"""
id: str = field(validator=instance_of(str))
@@ -35,19 +51,37 @@ class TopClass(Base):
float_like_optional: int = field(
default=None, validator=optional(instance_of(float)), converter=convert2float
)
+ float_like_optional2: int = field(
+ default=None, validator=optional(instance_of(float)), converter=convert2float
+ )
+
+ mid: MidClassNoId = field(
+ default=None, validator=optional(instance_of(MidClassNoId))
+ )
tc = TopClass(
- id="MyTest", float_like_req="03"
+ id="MyTest", float_like_req="04"
) # a string which can be converted to a float...
# tc.float_like_req = 2.01
-tc.float_like_optional = 43
+tc.float_like_optional = 44
+tc.float_like_optional2 = 66
+# tc.mid = MidClassNoId(int_val=4, str_val="three")
print(tc)
tc.to_yaml_file("test_instance.yaml")
+tc.to_json_file("test_instance.json")
+tc.to_xml_file("test_instance.xml")
+
+str_b4 = str(tc)
+
+str_json_after = TopClass.from_json_file("test_instance.json")
+print(str_json_after)
+str_yaml_after = TopClass.from_yaml_file("test_instance.yaml")
+print(str_yaml_after)
doc_md = tc.generate_documentation(format="markdown")
diff --git a/examples/test/test.specification.yaml b/examples/test/test.specification.yaml
index ca4fe7b2..07ee7946 100644
--- a/examples/test/test.specification.yaml
+++ b/examples/test/test.specification.yaml
@@ -10,3 +10,18 @@ TopClass:
float_like_optional:
type: int
description: name also says it all...
+ float_like_optional2:
+ type: int
+ description: name also says it all...
+ mid:
+ type: MidClassNoId
+ description: ''
+MidClassNoId:
+ definition: A model....
+ allowed_parameters:
+ int_val:
+ type: int
+ description: name says it all...
+ str_val:
+ type: int
+ description: name says it all...
diff --git a/examples/test/test_instance.json b/examples/test/test_instance.json
new file mode 100644
index 00000000..05941ece
--- /dev/null
+++ b/examples/test/test_instance.json
@@ -0,0 +1,7 @@
+{
+ "MyTest": {
+ "float_like_req": 4.0,
+ "float_like_optional": 44.0,
+ "float_like_optional2": 66.0
+ }
+}
diff --git a/examples/test/test_instance.xml b/examples/test/test_instance.xml
new file mode 100644
index 00000000..facb4731
--- /dev/null
+++ b/examples/test/test_instance.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/test/test_instance.yaml b/examples/test/test_instance.yaml
index 51f8b261..c5497868 100644
--- a/examples/test/test_instance.yaml
+++ b/examples/test/test_instance.yaml
@@ -1,3 +1,4 @@
MyTest:
- float_like_req: 3.0
- float_like_optional: 43.0
+ float_like_req: 4.0
+ float_like_optional: 44.0
+ float_like_optional2: 66.0
diff --git a/src/modelspec/base_types.py b/src/modelspec/base_types.py
index f4a81a9d..8f9d64b8 100644
--- a/src/modelspec/base_types.py
+++ b/src/modelspec/base_types.py
@@ -156,11 +156,26 @@ def from_dict(cls, d: Dict[str, Any]) -> "Base":
else:
return converter.structure(d, cls)
+ @classmethod
+ def from_yaml(cls, yaml_str: str) -> "Base":
+ """Instantiate an modelspec object from a YAML string"""
+ return cls.from_dict(yaml.load(yaml_str, Loader=yaml.SafeLoader))
+
+ @classmethod
+ def from_yaml_file(cls, yaml_file: str) -> "Base":
+ """Instantiate an modelspec object from a file containing YAML"""
+ return cls.from_dict(yaml.load(yaml_str, Loader=yaml.SafeLoader))
+
@classmethod
def from_json(cls, json_str: str) -> "Base":
"""Instantiate an modelspec object from a JSON string"""
return cls.from_dict(json.loads(json_str))
+ @classmethod
+ def from_json_file(cls, json_file: str) -> "Base":
+ """Instantiate an modelspec object from a file containing JSON"""
+ return cls.from_dict(json.load(json_file))
+
@classmethod
def from_bson(cls, bson_str: str) -> "Base":
"""Instantiate an modelspec object from a BSON string"""
diff --git a/tests/test_base.py b/tests/test_base.py
index 54a5bbb4..1237f254 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -162,8 +162,8 @@ def test_save_load_json(tmp_path):
# net.id = net.id+'_yaml'
net.to_yaml_file(filenamey)
- # filenamex = str(Path(tmp_path) / f"{net.id}.xml")
- # net.to_xml_file(filenamex)
+ filenamex = str(Path(tmp_path) / f"{net.id}.xml")
+ net.to_xml_file(filenamex)
from modelspec.utils import load_json, load_yaml, load_xml
@@ -173,12 +173,24 @@ def test_save_load_json(tmp_path):
str_netj = str(netj)
+ netj2 = NewNetwork.from_json(net.to_json())
+ str_netj2 = str(netj2)
+
+ netj3 = NewNetwork.from_json_file(filenamej)
+ str_netj3 = str(netj3)
+
datay = load_yaml(filenamey)
print_v("Loaded network specification from %s" % filenamey)
nety = NewNetwork.from_dict(datay)
str_nety = str(nety)
+ nety2 = NewNetwork.from_yaml(net.to_yaml() + " ")
+ str_nety2 = str(nety2)
+
+ nety3 = NewNetwork.from_yaml_file(filenamey)
+ str_nety3 = str(nety3)
+
# datax = load_xml(filenamex)
# print_v("Loaded network specification from %s" % filenamex)
@@ -201,6 +213,8 @@ def test_save_load_json(tmp_path):
) # Order not preserved in py2, just test len
else:
assert str_orig == str_netj
+ assert str_orig == str_netj2
+ assert str_orig == str_netj3
print("Test YAML..")
if sys.version_info[0] == 2:
@@ -209,6 +223,8 @@ def test_save_load_json(tmp_path):
) # Order not preserved in py2, just test len
else:
assert str_orig == str_nety
+ assert str_orig == str_nety2
+ assert str_orig == str_nety3
# print("Test XML..")
# if sys.version_info[0] == 2:
From d65bc18b32df78875c9d234ddd2b9a6119ccdc23 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Mon, 18 Sep 2023 15:48:17 +0100
Subject: [PATCH 14/32] Minimal working sbml example
Update a number of print statements
---
docs/sphinx/source/api/examples/document.xml | 4 +-
examples/document.xml | 4 +-
examples/neuroml2/TestNeuroML.xml | 2 +-
examples/neuroml2/neuroml2_spec.py | 2 +-
examples/sbml/regenerateAndTest.sh | 7 +
examples/sbml/test_minimal_example.json | 23 ++--
examples/sbml/test_minimal_example.xml | 4 +
examples/sbml/test_minimal_example.yaml | 7 +
examples/sbml/test_sbml3.py | 19 ++-
examples/test/test.md | 8 +-
examples/test/test.py | 27 ++--
examples/test/test.specification.yaml | 8 +-
examples/test/test_instance.json | 5 +-
examples/test/test_instance.xml | 6 +-
examples/test/test_instance.yaml | 4 +-
src/modelspec/base_types.py | 20 ++-
src/modelspec/utils.py | 128 ++++++++++---------
17 files changed, 158 insertions(+), 120 deletions(-)
create mode 100755 examples/sbml/regenerateAndTest.sh
create mode 100644 examples/sbml/test_minimal_example.xml
create mode 100644 examples/sbml/test_minimal_example.yaml
diff --git a/docs/sphinx/source/api/examples/document.xml b/docs/sphinx/source/api/examples/document.xml
index 4641d46e..164039f1 100644
--- a/docs/sphinx/source/api/examples/document.xml
+++ b/docs/sphinx/source/api/examples/document.xml
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/examples/document.xml b/examples/document.xml
index 4641d46e..164039f1 100644
--- a/examples/document.xml
+++ b/examples/document.xml
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/examples/neuroml2/TestNeuroML.xml b/examples/neuroml2/TestNeuroML.xml
index 70586cf6..6319bb51 100644
--- a/examples/neuroml2/TestNeuroML.xml
+++ b/examples/neuroml2/TestNeuroML.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/examples/neuroml2/neuroml2_spec.py b/examples/neuroml2/neuroml2_spec.py
index 6b07fda5..36422ab5 100644
--- a/examples/neuroml2/neuroml2_spec.py
+++ b/examples/neuroml2/neuroml2_spec.py
@@ -197,7 +197,7 @@ class neuroml(Base):
with open("NeuroML2.specification.yaml", "w") as d:
yy = yaml.dump(doc_dict, indent=4, sort_keys=False)
- print(yy)
+ # print(yy)
d.write(yy)
from modelspec.utils import load_xml
diff --git a/examples/sbml/regenerateAndTest.sh b/examples/sbml/regenerateAndTest.sh
new file mode 100755
index 00000000..198da2b6
--- /dev/null
+++ b/examples/sbml/regenerateAndTest.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+set -ex
+
+python test_sbml3.py
+
+python sbml_validators.py example_sbml_minimal.xml
+python sbml_validators.py test_minimal_example.xml
diff --git a/examples/sbml/test_minimal_example.json b/examples/sbml/test_minimal_example.json
index 27e2a1f9..75b6be4c 100644
--- a/examples/sbml/test_minimal_example.json
+++ b/examples/sbml/test_minimal_example.json
@@ -1,18 +1,11 @@
{
- "model": {
- "substanceUnits": "mole",
- "timeUnits": "second",
- "extentUnits": "mole",
- "listOfUnitDefinitions": [
- {
- "sid": "per_second",
- "listOfUnits": [
- {
- "kind": "second",
- "exponent": -1.0
- }
- ]
- }
- ]
+ "test_minimal_example": {
+ "level": "3",
+ "version": "2",
+ "model": {
+ "substanceUnits": "mole",
+ "timeUnits": "second",
+ "extentUnits": "mole"
+ }
}
}
diff --git a/examples/sbml/test_minimal_example.xml b/examples/sbml/test_minimal_example.xml
new file mode 100644
index 00000000..4ee2729c
--- /dev/null
+++ b/examples/sbml/test_minimal_example.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/examples/sbml/test_minimal_example.yaml b/examples/sbml/test_minimal_example.yaml
new file mode 100644
index 00000000..c07f26c5
--- /dev/null
+++ b/examples/sbml/test_minimal_example.yaml
@@ -0,0 +1,7 @@
+test_minimal_example:
+ level: '3'
+ version: '2'
+ model:
+ substanceUnits: mole
+ timeUnits: second
+ extentUnits: mole
diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py
index 1ee6dc18..0f93ff62 100755
--- a/examples/sbml/test_sbml3.py
+++ b/examples/sbml/test_sbml3.py
@@ -15,22 +15,27 @@
def test_example_sbml_minimal():
"aiming to match the xml file example_sbml_minimal.xml"
- path = "test_minimal_example"
+ name = "test_minimal_example"
- sbml_doc = SBML()
+ sbml_doc = sbml(id=name, version="2", level="3")
# open(f"{path}.docs.json", "w").write(json.dumps(sbml_doc.generate_documentation(format="dict"), indent=4))
model = Model(substanceUnits="mole", timeUnits="second", extentUnits="mole")
sbml_doc.model = model
- unit_def = UnitDefinition(sid="per_second")
+ unit_def = unitDefinition(sid="per_second")
+ """
model.listOfUnitDefinitions.append(unit_def)
-
unit = Unit(kind="second", exponent=-1.0)
- unit_def.listOfUnits.append(unit)
+ unit_def.listOfUnits.append(unit)"""
+
+ print("------------------------------------------------")
+ print(sbml_doc)
+ print("------------------------------------------------")
- sbml_doc.to_json_file(f"{path}.json")
- sbml_doc.to_xml_file(f"{path}.xml")
+ sbml_doc.to_json_file(f"{name}.json")
+ sbml_doc.to_yaml_file(f"{name}.yaml")
+ sbml_doc.to_xml_file(f"{name}.xml")
if __name__ == "__main__":
diff --git a/examples/test/test.md b/examples/test/test.md
index f81c64cd..ccfd2fe6 100644
--- a/examples/test/test.md
+++ b/examples/test/test.md
@@ -12,20 +12,20 @@ A model....
float_like_req |
- int |
+ float |
name says it all... |
float_like_optional |
- int |
+ float |
name also says it all... |
- float_like_optional2 |
+ int_like_optional |
int |
name also says it all... |
@@ -54,7 +54,7 @@ A model....
str_val |
- int |
+ str |
name says it all... |
diff --git a/examples/test/test.py b/examples/test/test.py
index b52e23c1..a995ec2b 100644
--- a/examples/test/test.py
+++ b/examples/test/test.py
@@ -10,7 +10,7 @@
def convert2float(x: Any) -> float:
- print("Converting {} ({})".format(x, type(x)))
+ print("convert2float {} ({})".format(x, type(x)))
"""Convert to float if not None"""
if x is not None:
return float(x)
@@ -18,6 +18,15 @@ def convert2float(x: Any) -> float:
return None
+def convert2int(x: Any) -> int:
+ print("convert2int {} ({})".format(x, type(x)))
+ """Convert to int if not None"""
+ if x is not None:
+ return int(x)
+ else:
+ return None
+
+
@modelspec.define
class MidClassNoId(Base):
"""
@@ -29,7 +38,7 @@ class MidClassNoId(Base):
"""
int_val: int = field(default=None, validator=instance_of(int))
- str_val: int = field(default=None, validator=optional(instance_of(str)))
+ str_val: str = field(default=None, validator=optional(instance_of(str)))
@modelspec.define
@@ -41,18 +50,18 @@ class TopClass(Base):
id: The unique id of the thing
float_like_req: name says it all...
float_like_optional: name also says it all...
- float_like_optional2: name also says it all...
+ int_like_optional: name also says it all...
"""
id: str = field(validator=instance_of(str))
- float_like_req: int = field(
+ float_like_req: float = field(
default=None, validator=instance_of(float), converter=convert2float
)
- float_like_optional: int = field(
+ float_like_optional: float = field(
default=None, validator=optional(instance_of(float)), converter=convert2float
)
- float_like_optional2: int = field(
- default=None, validator=optional(instance_of(float)), converter=convert2float
+ int_like_optional: int = field(
+ default=None, validator=optional(instance_of(int)), converter=convert2int
)
mid: MidClassNoId = field(
@@ -66,8 +75,8 @@ class TopClass(Base):
# tc.float_like_req = 2.01
tc.float_like_optional = 44
-tc.float_like_optional2 = 66
-# tc.mid = MidClassNoId(int_val=4, str_val="three")
+# tc.float_like_optional2 = 66
+tc.mid = MidClassNoId(int_val=4, str_val="three")
print(tc)
diff --git a/examples/test/test.specification.yaml b/examples/test/test.specification.yaml
index 07ee7946..4ffaa7aa 100644
--- a/examples/test/test.specification.yaml
+++ b/examples/test/test.specification.yaml
@@ -5,12 +5,12 @@ TopClass:
type: str
description: The unique id of the thing
float_like_req:
- type: int
+ type: float
description: name says it all...
float_like_optional:
- type: int
+ type: float
description: name also says it all...
- float_like_optional2:
+ int_like_optional:
type: int
description: name also says it all...
mid:
@@ -23,5 +23,5 @@ MidClassNoId:
type: int
description: name says it all...
str_val:
- type: int
+ type: str
description: name says it all...
diff --git a/examples/test/test_instance.json b/examples/test/test_instance.json
index 05941ece..7b8b0878 100644
--- a/examples/test/test_instance.json
+++ b/examples/test/test_instance.json
@@ -2,6 +2,9 @@
"MyTest": {
"float_like_req": 4.0,
"float_like_optional": 44.0,
- "float_like_optional2": 66.0
+ "mid": {
+ "int_val": 4,
+ "str_val": "three"
+ }
}
}
diff --git a/examples/test/test_instance.xml b/examples/test/test_instance.xml
index facb4731..4895ba5e 100644
--- a/examples/test/test_instance.xml
+++ b/examples/test/test_instance.xml
@@ -1,2 +1,4 @@
-
-
+
+
+
+
diff --git a/examples/test/test_instance.yaml b/examples/test/test_instance.yaml
index c5497868..1a3bcd49 100644
--- a/examples/test/test_instance.yaml
+++ b/examples/test/test_instance.yaml
@@ -1,4 +1,6 @@
MyTest:
float_like_req: 4.0
float_like_optional: 44.0
- float_like_optional2: 66.0
+ mid:
+ int_val: 4
+ str_val: three
diff --git a/src/modelspec/base_types.py b/src/modelspec/base_types.py
index 8f9d64b8..5dbe8f48 100644
--- a/src/modelspec/base_types.py
+++ b/src/modelspec/base_types.py
@@ -57,11 +57,13 @@ def print_(text: str, print_it: bool = False):
"""
Print a message preceded by modelspec, only if print_it=True
"""
+ if not print_it:
+ return
+
prefix = "modelspec >>> "
if not isinstance(text, str):
text = ("%s" % text).decode("ascii")
- if print_it:
- print("{}{}".format(prefix, text.replace("\n", "\n" + prefix)))
+ print("{}{}".format(prefix, text.replace("\n", "\n" + prefix)))
def print_v(text: str):
@@ -128,11 +130,14 @@ def to_xml(self) -> str:
root = build_xml_element(self)
xml_string = ET.tostring(
- root, encoding="utf-8", xml_declaration=False, method="xml"
+ root, encoding="UTF-8", xml_declaration=True, method="xml"
).decode("utf-8")
parsed_xml = xml.dom.minidom.parseString(xml_string)
pretty_xml = parsed_xml.toprettyxml(indent=" " * 4)
+ pretty_xml = pretty_xml.replace(
+ '?xml version="1.0" ?', '?xml version="1.0" encoding="UTF-8"?'
+ )
return pretty_xml
@classmethod
@@ -318,14 +323,7 @@ def to_xml_file(
if filename is None:
filename = f"{self.id}.xml"
- root = build_xml_element(self)
-
- # Generate the XML string
- xml_str = ET.tostring(root, encoding="utf-8", method="xml").decode("utf-8")
-
- # Create a pretty-formatted XML string using minidom
- parsed_xml = xml.dom.minidom.parseString(xml_str)
- pretty_xml_str = parsed_xml.toprettyxml(indent=" " * 4)
+ pretty_xml_str = self.to_xml()
# Write the XML data to the file
with open(filename, "w", encoding="utf-8") as file:
diff --git a/src/modelspec/utils.py b/src/modelspec/utils.py
index 16844cb1..29f9275a 100644
--- a/src/modelspec/utils.py
+++ b/src/modelspec/utils.py
@@ -14,7 +14,7 @@
from modelspec.base_types import EvaluableExpression
from random import Random
-from typing import Union
+from typing import Union, Dict
verbose = False
@@ -247,8 +247,12 @@ def build_xml_element(data, parent=None):
if parent is None:
parent = ET.Element(data.__class__.__name__)
+ print_(" == Converting to XML: %s" % data, verbose)
+
attrs = attr.fields(data.__class__)
for aattr in attrs:
+
+ print_(" == Looking at: {} ({})".format(aattr, type(aattr)), verbose)
if isinstance(aattr.default, attr.Factory):
children = data.__getattribute__(aattr.name)
if not isinstance(children, (list, tuple)):
@@ -258,11 +262,32 @@ def build_xml_element(data, parent=None):
child_element = build_xml_element(child)
parent.append(child_element)
- # Filters name space and schemaLoacation attributes, only allows non name space attributes to be added as attributes
+ # Filters name space and schemaLocation attributes, only allows non name space attributes to be added as attributes
elif not isinstance(aattr.default, str):
attribute_name = aattr.name
attribute_value = data.__getattribute__(aattr.name)
- parent.set(attribute_name, str(attribute_value))
+ print_(
+ f" -- {attribute_name} = {attribute_value} (is type: {type(attribute_value)}, should be type:{aattr.type}))",
+ verbose,
+ )
+ if attribute_value is not None:
+ if (
+ type(attribute_value) == int
+ or type(attribute_value) == float
+ or type(attribute_value) == str
+ or type(attribute_value) == bool
+ or type(attribute_value) == list
+ ):
+ parent.set(attribute_name, str(attribute_value))
+ elif type(attribute_value) == dict:
+
+ """for k, v in attribute_value.items():
+ child_element = build_xml_element(v)"""
+ else:
+ child_element = build_xml_element(attribute_value)
+ child_element.tag = attribute_name
+ #
+ parent.append(child_element)
# This defines the various namespaces and schemaLocation of the generated xml
if hasattr(data, "xmlns"):
@@ -285,13 +310,12 @@ def ascii_encode_dict(data):
def _parse_element(dict_format, to_build):
- if verbose:
- print("Parse for element: [%s]" % dict_format)
+ print_("Parse for element: [%s]" % dict_format, verbose)
for k in dict_format.keys():
- if verbose:
- print(
- " Setting id: {} in {} ({})".format(k, type.__name__, type(to_build))
- )
+ print_(
+ " Setting id: {} in {} ({})".format(k, type.__name__, type(to_build)),
+ verbose,
+ )
to_build.id = k
to_build = _parse_attributes(dict_format[k], to_build)
@@ -303,10 +327,10 @@ def _parse_attributes(dict_format, to_build):
for key in dict_format:
value = dict_format[key]
new_format = True
- if verbose:
- print(
- " Setting {}={} ({}) in {}".format(key, value, type(value), to_build)
- )
+ print_(
+ " Setting {}={} ({}) in {}".format(key, value, type(value), to_build),
+ verbose,
+ )
if new_format:
if type(to_build) == dict:
@@ -316,8 +340,7 @@ def _parse_attributes(dict_format, to_build):
type_to_use = to_build.allowed_children[key][1]
for v in value:
ff = type_to_use()
- if verbose:
- print(f" Type for {key}: {type_to_use} ({ff})")
+ print_(f" Type for {key}: {type_to_use} ({ff})", verbose)
ff = _parse_element({v: value[v]}, ff)
exec("to_build.%s.append(ff)" % key)
else:
@@ -332,13 +355,11 @@ def _parse_attributes(dict_format, to_build):
to_build.__setattr__(key, value)
else:
type_to_use = to_build.allowed_fields[key][1]
- if verbose:
- print(
- "type_to_use: {} ({})".format(
- type_to_use, type(type_to_use)
- )
- )
- print(f"- {key} = {value}")
+ print_(
+ "type_to_use: {} ({})".format(type_to_use, type(type_to_use)),
+ verbose,
+ )
+ print_(f"- {key} = {value}", verbose)
if type_to_use == EvaluableExpression:
vv = {}
@@ -449,19 +470,17 @@ def evaluate(
if array_format == FORMAT_TENSORFLOW:
import tensorflow as tf
- if verbose:
- print_(
- " > Evaluating: [%s] which is a: %s, vs parameters: %s (using %s arrays)..."
- % (expr, type(expr).__name__, _params_info(parameters), array_format),
- verbose,
- )
+ print_(
+ " > Evaluating: [%s] which is a: %s, vs parameters: %s (using %s arrays)..."
+ % (expr, type(expr).__name__, _params_info(parameters), array_format),
+ verbose,
+ )
try:
if type(expr) == str and expr in parameters:
expr = parameters[
expr
] # replace with the value in parameters & check whether it's float/int...
- if verbose:
- print_(" Using for that param: %s" % _val_info(expr), verbose)
+ print_(" Using for that param: %s" % _val_info(expr), verbose)
if type(expr) == str:
try:
@@ -480,41 +499,34 @@ def evaluate(
pass
if type(expr) == list:
- if verbose:
- print_(" Returning a list in format: %s" % array_format, verbose)
+ print_(" Returning a list in format: %s" % array_format, verbose)
if array_format == FORMAT_TENSORFLOW:
return tf.constant(expr, dtype=tf.float64)
else:
return np.array(expr)
if type(expr) == np.ndarray:
- if verbose:
- print_(
- " Returning a numpy array in format: %s" % array_format, verbose
- )
+ print_(" Returning a numpy array in format: %s" % array_format, verbose)
if array_format == FORMAT_TENSORFLOW:
return tf.convert_to_tensor(expr, dtype=tf.float64)
else:
return np.array(expr)
if "Tensor" in type(expr).__name__:
- if verbose:
- print_(
- " Returning a tensorflow Tensor in format: %s" % array_format,
- verbose,
- )
+ print_(
+ " Returning a tensorflow Tensor in format: %s" % array_format,
+ verbose,
+ )
if array_format == FORMAT_NUMPY:
return expr.numpy()
else:
return expr
if int(expr) == expr and cast_to_int:
- if verbose:
- print_(" Returning int: %s" % int(expr), verbose)
+ print_(" Returning int: %s" % int(expr), verbose)
return int(expr)
else: # will have failed if not number
- if verbose:
- print_(" Returning {}: {}".format(type(expr), expr), verbose)
+ print_(" Returning {}: {}".format(type(expr), expr), verbose)
return expr
except:
try:
@@ -531,25 +543,22 @@ def evaluate(
if type(expr) == str and "numpy." in expr:
parameters["numpy"] = np
- if verbose:
- print_(
- " Trying to eval [%s] with Python using %s..."
- % (expr, parameters.keys()),
- verbose,
- )
+ print_(
+ " Trying to eval [%s] with Python using %s..."
+ % (expr, parameters.keys()),
+ verbose,
+ )
v = eval(expr, parameters)
- if verbose:
- print_(
- " Evaluated with Python: {} = {}".format(expr, _val_info(v)),
- verbose,
- )
+ print_(
+ " Evaluated with Python: {} = {}".format(expr, _val_info(v)),
+ verbose,
+ )
if (type(v) == float or type(v) == str) and int(v) == v:
- if verbose:
- print_(" Returning int: %s" % int(v), verbose)
+ print_(" Returning int: %s" % int(v), verbose)
if array_format == FORMAT_TENSORFLOW:
return tf.constant(int(v))
@@ -557,8 +566,7 @@ def evaluate(
return int(v)
return v
except Exception as e:
- if verbose:
- print_(f" Returning without altering: {expr} (error: {e})", verbose)
+ print_(f" Returning without altering: {expr} (error: {e})", verbose)
return expr
From af43a938165fe2a1b71025174386122812024c46 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Mon, 18 Sep 2023 15:50:01 +0100
Subject: [PATCH 15/32] Updates to core sbml model definition
---
docs/sphinx/source/api/Contributors.md | 2 +-
examples/sbml/example_sbml_minimal.xml | 2 +-
examples/sbml/sbml32spec.py | 20 +++++---
examples/sbml/sbml_validators.py | 69 ++++++++++++++++++++++++--
test_all.sh | 5 ++
5 files changed, 86 insertions(+), 12 deletions(-)
diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md
index 969b553c..4885bfc2 100644
--- a/docs/sphinx/source/api/Contributors.md
+++ b/docs/sphinx/source/api/Contributors.md
@@ -3,7 +3,7 @@
# Modelspec contributors
This page list names and Github profiles of contributors to Modelspec, listed in no particular order.
-This page is generated periodically, most recently on 2023-09-13.
+This page is generated periodically, most recently on 2023-09-18.
- Padraig Gleeson ([@pgleeson](https://github.com/pgleeson))
- Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin))
diff --git a/examples/sbml/example_sbml_minimal.xml b/examples/sbml/example_sbml_minimal.xml
index fe9b1524..f1a8fdfe 100644
--- a/examples/sbml/example_sbml_minimal.xml
+++ b/examples/sbml/example_sbml_minimal.xml
@@ -1,5 +1,5 @@
-
+
diff --git a/examples/sbml/sbml32spec.py b/examples/sbml/sbml32spec.py
index 997607e0..1716b285 100644
--- a/examples/sbml/sbml32spec.py
+++ b/examples/sbml/sbml32spec.py
@@ -66,6 +66,7 @@ class SBase(Base):
sid: str = field(default=None, validator=optional([instance_of(str), valid_sid]))
name: str = field(default=None, validator=optional(instance_of(str)))
+
metaid: str = field(
default=None, validator=optional([instance_of(str), valid_xml_id])
)
@@ -79,6 +80,12 @@ class SBase(Base):
)
+@modelspec.define
+class SBaseWithId(SBase):
+
+ id: str = field(default=None, validator=optional([instance_of(str), valid_sid]))
+
+
@modelspec.define
class Trigger(SBase):
initialValue: bool = field(default=None, validator=instance_of(bool))
@@ -345,7 +352,7 @@ class Unit(SBase):
@modelspec.define
-class UnitDefinition(SBase):
+class unitDefinition(SBase):
"""
A unit definition
@@ -412,7 +419,7 @@ class Model(SBase):
)
listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list)
- listOfUnitDefinitions: List[UnitDefinition] = field(factory=list)
+ listOfUnitDefinitions: List[unitDefinition] = field(factory=list)
listOfCompartments: List[Compartment] = field(factory=list)
listOfSpecies: List[Species] = field(factory=list)
listOfParameters: List[Parameter] = field(factory=list)
@@ -424,7 +431,7 @@ class Model(SBase):
@modelspec.define
-class SBML(SBase):
+class sbml(SBaseWithId):
"""
The top-level SBML container implementing SBML 3.2.
See sbml.level-3.version-2.core.release-2.pdf section 4.
@@ -439,10 +446,11 @@ class SBML(SBase):
"""
xmlns: str = field(
- default="http://www.sbml.org/sbml/level3/version2/core",
+ default="http://www.sbml.org/sbml/level3/version%s/core" % SBML_VERSION,
validator=[instance_of(str), xmlns_sbml],
)
- level: str = field(default="3", validator=[instance_of(str), fixed_level])
- version: str = field(default="2", validator=[instance_of(str), fixed_version])
+ level: str = field(default=None, validator=[instance_of(str), fixed_level])
+ version: str = field(default=None, validator=[instance_of(str), fixed_version])
model: Model = field(default=None, validator=optional(instance_of(Model)))
+ # models: List[Model] = field(factory=list)
diff --git a/examples/sbml/sbml_validators.py b/examples/sbml/sbml_validators.py
index 73894926..941704f2 100644
--- a/examples/sbml/sbml_validators.py
+++ b/examples/sbml/sbml_validators.py
@@ -6,6 +6,7 @@
import re
from lxml import etree
from io import StringIO
+import sys
# sbml.level-3.version-2.core.release-2.pdf Table 2
sbml_si_units = """
@@ -13,6 +14,9 @@
second tesla becquerel farad hertz kelvin lux ohm siemens volt candela gram item kilogram metre pascal sievert watt
""".split()
+SBML_VERSION = "2"
+SBML_LEVEL = "3"
+
def valid_kind(instance, attribute, value):
if not value in sbml_si_units:
@@ -43,13 +47,13 @@ def xmlns_math(instance, attribute, value):
def fixed_level(instance, attribute, value):
- if value != "3":
- raise ValueError("this implementation only supports level 3")
+ if value != SBML_LEVEL:
+ raise ValueError("this implementation only supports level %s" % SBML_LEVEL)
def fixed_version(instance, attribute, value):
- if value != "2":
- raise ValueError("this implementation only supports level 2")
+ if value != SBML_VERSION:
+ raise ValueError("this implementation only supports version %s" % SBML_VERSION)
def valid_sid(instance, attribute, value):
@@ -114,3 +118,60 @@ def valid_mathml(instance, attribute, value):
raise ValueError(
f"{value} doesn't look like MathML (this validator is only a stub)"
)
+
+
+# from: https://github.com/combine-org/combine-notebooks
+
+
+def validate_sbml(doc, units_consistency: bool = False) -> None:
+ """Validate sbml."""
+ import libsbml
+
+ # set the unit checking, similar for the other settings
+ doc.setConsistencyChecks(libsbml.LIBSBML_CAT_UNITS_CONSISTENCY, units_consistency)
+ doc.checkConsistency()
+ # get errors/warnings
+ n_errors: int = doc.getNumErrors()
+ errors: List[libsbml.SBMLError] = list()
+ warnings: List[libsbml.SBMLError] = list()
+ for k in range(n_errors):
+ error: libsbml.SBMLError = doc.getError(k)
+ severity = error.getSeverity()
+ if (severity == libsbml.LIBSBML_SEV_ERROR) or (
+ severity == libsbml.LIBSBML_SEV_FATAL
+ ):
+ errors.append(error)
+ else:
+ warnings.append(error)
+ # print results
+ print("-" * 80)
+ print(f"{'validation error(s)':<25}: {len(errors)}")
+ print(f"{'validation warning(s)':<25}: {len(warnings)}")
+ if len(errors) > 0:
+ print("--- errors ---")
+ for kerr in enumerate(errors):
+ print(f"E{kerr}: {error.getCategoryAsString()} L{error.getLine()}")
+ print(
+ f"[{error.getSeverityAsString()}] {error.getShortMessage()} | {error.getMessage()}"
+ )
+ if len(warnings) > 0:
+ print("--- warnings ---")
+ for kwarn in enumerate(warnings):
+ print(f"E{kwarn}: {error.getCategoryAsString()} L{error.getLine()}")
+ print(
+ f"[{error.getSeverityAsString()}] {error.getShortMessage()} | {error.getMessage()}"
+ )
+ print("-" * 80)
+
+
+if __name__ == "__main__":
+
+ import libsbml
+
+ sbml_file = sys.argv[1]
+
+ print("Going to validate SBML file: %s" % sbml_file)
+ reader = libsbml.SBMLReader()
+ document = reader.readSBML(sbml_file)
+
+ validate_sbml(document, units_consistency=False)
diff --git a/test_all.sh b/test_all.sh
index 0834fcd2..f646a3e4 100755
--- a/test_all.sh
+++ b/test_all.sh
@@ -24,6 +24,11 @@ python neuroml2_spec.py
pynml -validate hello_world_neuroml.net.nml
pynml -validate TestNeuroML.xml
+## Test SBML example
+
+cd ../sbml
+./regenerateAndTest.sh
+
cd ../..
From 5c1eba6edefdcbd5e1cf0d99bf8cc1dc22a55421 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Mon, 18 Sep 2023 16:36:10 +0100
Subject: [PATCH 16/32] Don't test xml examples on py3.7
---
.github/workflows/ci.yml | 13 ++++++++++---
examples/test/test.py | 9 ++++++---
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c3d8cf85..49374e01 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -47,7 +47,7 @@ jobs:
run: |
pip list
- - name: Run some examples
+ - name: Run simple examples
run: |
cd examples
python document.py
@@ -55,13 +55,20 @@ jobs:
cd test
python test.py
- ## Test NeuroML example
+ - name: Test NeuroML examples
+ if: ${{ matrix.python-version != '3.7'}}
- cd ../neuroml2
+ cd examples/neuroml2
python neuroml2_spec.py
# Note: NeuroML files will be validated with OMV below
+ - name: Test SBML examples
+ if: ${{ matrix.python-version != '3.7'}}
+
+ cd examples/sbml
+ ./regenerateAndTest.sh
+
- name: Run pytest
run: |
pytest tests
diff --git a/examples/test/test.py b/examples/test/test.py
index a995ec2b..ce820a7f 100644
--- a/examples/test/test.py
+++ b/examples/test/test.py
@@ -3,10 +3,11 @@
from modelspec.base_types import Base
from modelspec.utils import load_json
from typing import List
+from typing import Any
+import sys
-# Example testing multiple options...
-from typing import Any
+# Example testing multiple options...
def convert2float(x: Any) -> float:
@@ -83,7 +84,9 @@ class TopClass(Base):
tc.to_yaml_file("test_instance.yaml")
tc.to_json_file("test_instance.json")
-tc.to_xml_file("test_instance.xml")
+
+if sys.version_info >= (3, 8):
+ tc.to_xml_file("test_instance.xml")
str_b4 = str(tc)
From 53c86e9cf01c52ba7c76030a1a06cf328c637706 Mon Sep 17 00:00:00 2001
From: Padraig Gleeson
Date: Mon, 18 Sep 2023 16:38:32 +0100
Subject: [PATCH 17/32] Update ci.yml
---
.github/workflows/ci.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 49374e01..49c77d85 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -57,6 +57,7 @@ jobs:
- name: Test NeuroML examples
if: ${{ matrix.python-version != '3.7'}}
+ run: |
cd examples/neuroml2
python neuroml2_spec.py
@@ -65,6 +66,7 @@ jobs:
- name: Test SBML examples
if: ${{ matrix.python-version != '3.7'}}
+ run: |
cd examples/sbml
./regenerateAndTest.sh
From f5c7445bb50472ee11f1870d8b1b71b706d404ae Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Mon, 18 Sep 2023 17:16:18 +0100
Subject: [PATCH 18/32] Add sbml requirements
---
setup.cfg | 1 +
tests/test_base.py | 5 +++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/setup.cfg b/setup.cfg
index 8d8f84cf..cc44eee2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -84,6 +84,7 @@ dev =
flake8
pyneuroml>=0.7.2
NeuroMLlite>=0.5.3
+ python-libsbml
modelspec[test]
all =
diff --git a/tests/test_base.py b/tests/test_base.py
index 1237f254..3cd6fddb 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -162,8 +162,9 @@ def test_save_load_json(tmp_path):
# net.id = net.id+'_yaml'
net.to_yaml_file(filenamey)
- filenamex = str(Path(tmp_path) / f"{net.id}.xml")
- net.to_xml_file(filenamex)
+ if sys.version_info >= (3, 8):
+ filenamex = str(Path(tmp_path) / f"{net.id}.xml")
+ net.to_xml_file(filenamex)
from modelspec.utils import load_json, load_yaml, load_xml
From 1064af305feef66376c0b836eeb91066780f2e66 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Mon, 18 Sep 2023 18:33:11 +0100
Subject: [PATCH 19/32] Generates same example sbml; restructured handling of
listOf... classes
Can certainly be made more succinct, but generating valid SBML
---
examples/sbml/example_sbml_minimal.xml | 3 ++
examples/sbml/sbml32spec.py | 42 ++++++++++++++++++++-----
examples/sbml/sbml_validators.py | 15 ++++++---
examples/sbml/test_minimal_example.json | 16 +++++++++-
examples/sbml/test_minimal_example.xml | 10 +++++-
examples/sbml/test_minimal_example.yaml | 7 +++++
examples/sbml/test_sbml3.py | 14 ++++++---
7 files changed, 88 insertions(+), 19 deletions(-)
diff --git a/examples/sbml/example_sbml_minimal.xml b/examples/sbml/example_sbml_minimal.xml
index f1a8fdfe..c3315f41 100644
--- a/examples/sbml/example_sbml_minimal.xml
+++ b/examples/sbml/example_sbml_minimal.xml
@@ -1,5 +1,8 @@
+
+
+
diff --git a/examples/sbml/sbml32spec.py b/examples/sbml/sbml32spec.py
index 1716b285..c6372a91 100644
--- a/examples/sbml/sbml32spec.py
+++ b/examples/sbml/sbml32spec.py
@@ -333,7 +333,7 @@ class Compartment(SBase):
@modelspec.define
-class Unit(SBase):
+class unit(SBase):
"""
A unit used to compose a unit definition.
unit = (multiplier x 10^scale x kind)^exponent
@@ -352,17 +352,41 @@ class Unit(SBase):
@modelspec.define
-class unitDefinition(SBase):
+class ListOfUnits(SBase):
+ """
+ A listOfUnits
+
+ Args:
+ listOfUnits: the actual list Of Units
+ """
+
+ listOfUnits: List[unit] = field(factory=list)
+
+
+@modelspec.define
+class unitDefinition(SBaseWithId):
"""
A unit definition
Args:
- sid: UnitSid required (overrides SBase sid)
listOfUnits: List of units used to compose the definition
"""
- sid: str = field(default=None, validator=[instance_of(str), valid_unitsid])
- listOfUnits: List[Unit] = field(factory=list)
+ listOfUnits: ListOfUnits = field(
+ default=None, validator=optional(instance_of(ListOfUnits))
+ )
+
+
+@modelspec.define
+class ListOfUnitDefinitions(SBase):
+ """
+ A listOfUnitDefinitions
+
+ Args:
+ listOfUnitDefinitions: the actual list Of Unit Definitions
+ """
+
+ listOfUnitDefinitions: List[unitDefinition] = field(factory=list)
@modelspec.define
@@ -419,7 +443,11 @@ class Model(SBase):
)
listOfFunctionDefinitions: List[FunctionDefinition] = field(factory=list)
- listOfUnitDefinitions: List[unitDefinition] = field(factory=list)
+
+ listOfUnitDefinitions: ListOfUnitDefinitions = field(
+ default=None, validator=optional(instance_of(ListOfUnitDefinitions))
+ )
+
listOfCompartments: List[Compartment] = field(factory=list)
listOfSpecies: List[Species] = field(factory=list)
listOfParameters: List[Parameter] = field(factory=list)
@@ -435,7 +463,7 @@ class sbml(SBaseWithId):
"""
The top-level SBML container implementing SBML 3.2.
See sbml.level-3.version-2.core.release-2.pdf section 4.
- http://www.sbml.org/sbml/level3/version2/core
+ http://www.sbml.org/sbml/level3/version2/
Args:
xmlns: string, fixed to "http://www.sbml.org/sbml/level3/version2/core"
diff --git a/examples/sbml/sbml_validators.py b/examples/sbml/sbml_validators.py
index 941704f2..5ab66a5b 100644
--- a/examples/sbml/sbml_validators.py
+++ b/examples/sbml/sbml_validators.py
@@ -57,10 +57,11 @@ def fixed_version(instance, attribute, value):
def valid_sid(instance, attribute, value):
- if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value):
- raise ValueError(
- "an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*"
- )
+ if value is not None:
+ if not re.fullmatch("[_A-Za-z][_A-Za-z0-9]*", value):
+ raise ValueError(
+ "an SId must match the regular expression: [_A-Za-z][_A-Za-z0-9]*"
+ )
def valid_unitsid(instance, attribute, value):
@@ -163,6 +164,8 @@ def validate_sbml(doc, units_consistency: bool = False) -> None:
)
print("-" * 80)
+ return len(warnings) + len(errors)
+
if __name__ == "__main__":
@@ -174,4 +177,6 @@ def validate_sbml(doc, units_consistency: bool = False) -> None:
reader = libsbml.SBMLReader()
document = reader.readSBML(sbml_file)
- validate_sbml(document, units_consistency=False)
+ issues = validate_sbml(document, units_consistency=False)
+
+ exit(issues)
diff --git a/examples/sbml/test_minimal_example.json b/examples/sbml/test_minimal_example.json
index 75b6be4c..e83375b6 100644
--- a/examples/sbml/test_minimal_example.json
+++ b/examples/sbml/test_minimal_example.json
@@ -5,7 +5,21 @@
"model": {
"substanceUnits": "mole",
"timeUnits": "second",
- "extentUnits": "mole"
+ "extentUnits": "mole",
+ "listOfUnitDefinitions": {
+ "listOfUnitDefinitions": {
+ "per_second": {
+ "listOfUnits": {
+ "listOfUnits": [
+ {
+ "kind": "second",
+ "exponent": -1.0
+ }
+ ]
+ }
+ }
+ }
+ }
}
}
}
diff --git a/examples/sbml/test_minimal_example.xml b/examples/sbml/test_minimal_example.xml
index 4ee2729c..29bf4ff4 100644
--- a/examples/sbml/test_minimal_example.xml
+++ b/examples/sbml/test_minimal_example.xml
@@ -1,4 +1,12 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/examples/sbml/test_minimal_example.yaml b/examples/sbml/test_minimal_example.yaml
index c07f26c5..e0916973 100644
--- a/examples/sbml/test_minimal_example.yaml
+++ b/examples/sbml/test_minimal_example.yaml
@@ -5,3 +5,10 @@ test_minimal_example:
substanceUnits: mole
timeUnits: second
extentUnits: mole
+ listOfUnitDefinitions:
+ listOfUnitDefinitions:
+ per_second:
+ listOfUnits:
+ listOfUnits:
+ - kind: second
+ exponent: -1.0
diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py
index 0f93ff62..50f595a3 100755
--- a/examples/sbml/test_sbml3.py
+++ b/examples/sbml/test_sbml3.py
@@ -23,11 +23,15 @@ def test_example_sbml_minimal():
model = Model(substanceUnits="mole", timeUnits="second", extentUnits="mole")
sbml_doc.model = model
- unit_def = unitDefinition(sid="per_second")
- """
- model.listOfUnitDefinitions.append(unit_def)
- unit = Unit(kind="second", exponent=-1.0)
- unit_def.listOfUnits.append(unit)"""
+ unit_def = unitDefinition(id="per_second")
+
+ model.listOfUnitDefinitions = ListOfUnitDefinitions()
+
+ model.listOfUnitDefinitions.listOfUnitDefinitions.append(unit_def)
+
+ unit_s = unit(kind="second", exponent=-1.0)
+ unit_def.listOfUnits = ListOfUnits()
+ unit_def.listOfUnits.listOfUnits.append(unit_s)
print("------------------------------------------------")
print(sbml_doc)
From 757a45031a3e05688412cac5037d243040d134c6 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Tue, 19 Sep 2023 10:33:54 +0100
Subject: [PATCH 20/32] Generate spec docs for sbml
---
.gitignore | 1 +
docs/sphinx/source/api/Contributors.md | 2 +-
examples/COMBINE.md | 20 +
examples/sbml/SBML.md | 2266 ++++++++++++++++++++++++
examples/sbml/SBML.rst | 885 +++++++++
examples/sbml/SBML.specification.json | 1006 +++++++++++
examples/sbml/SBML.specification.yaml | 735 ++++++++
examples/sbml/test_sbml3.py | 25 +
8 files changed, 4939 insertions(+), 1 deletion(-)
create mode 100644 examples/COMBINE.md
create mode 100644 examples/sbml/SBML.md
create mode 100644 examples/sbml/SBML.rst
create mode 100644 examples/sbml/SBML.specification.json
create mode 100644 examples/sbml/SBML.specification.yaml
diff --git a/.gitignore b/.gitignore
index 3ce81332..e7c235dc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -163,3 +163,4 @@ cython_debug/
.idea/
/examples/document.specification.bson
/examples/neuroml2/hello_world.v.dat
+/examples/sbml/example_sbml_test.xml
diff --git a/docs/sphinx/source/api/Contributors.md b/docs/sphinx/source/api/Contributors.md
index 4885bfc2..51d4661e 100644
--- a/docs/sphinx/source/api/Contributors.md
+++ b/docs/sphinx/source/api/Contributors.md
@@ -3,7 +3,7 @@
# Modelspec contributors
This page list names and Github profiles of contributors to Modelspec, listed in no particular order.
-This page is generated periodically, most recently on 2023-09-18.
+This page is generated periodically, most recently on 2023-09-19.
- Padraig Gleeson ([@pgleeson](https://github.com/pgleeson))
- Manifest Chakalov ([@mqnifestkelvin](https://github.com/mqnifestkelvin))
diff --git a/examples/COMBINE.md b/examples/COMBINE.md
new file mode 100644
index 00000000..72e6ad4e
--- /dev/null
+++ b/examples/COMBINE.md
@@ -0,0 +1,20 @@
+# COMBINE standards in modelspec
+
+To illustrate the utility of modelspec, and to create a uniform, compatible set of APIs across a diverse range of model specification formats, we are investigating the [COMBINE set of standards in computational biology](https://co.mbine.org/standards/), and attempting to express these formats in modelspec.
+
+## NeuroML
+
+A preliminary version of an API for [NeuroML2](https://docs.neuroml.org/Userdocs/NeuroMLv2.html) can be found [here](NeuroML).
+
+## SBML
+
+work towards an API for [SBML](https://www.sbml.org/) can be found [here](SBML).
+
+## CellML
+
+TODO...
+
+## SED-ML
+
+TODO...
+
diff --git a/examples/sbml/SBML.md b/examples/sbml/SBML.md
new file mode 100644
index 00000000..6d2eb680
--- /dev/null
+++ b/examples/sbml/SBML.md
@@ -0,0 +1,2266 @@
+## sbml
+The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf section 4.
+http://www.sbml.org/sbml/level3/version2/
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ id |
+ str |
+ |
+
+
+
+
+ xmlns |
+ str |
+ string, fixed to "http://www.sbml.org/sbml/level3/version2/core" |
+
+
+
+
+ level |
+ str |
+ SBML level, fixed to 3 |
+
+
+
+
+ version |
+ str |
+ SBML version, fixed to 2 |
+
+
+
+
+ model |
+ Model |
+ Optional model |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Model
+The model
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ substanceUnits |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+ timeUnits |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+ volumeUnits |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+ areaUnits |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+ lengthUnits |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+ extentUnits |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+ conversionFactor |
+ str |
+ SIdRef optional |
+
+
+
+
+ listOfUnitDefinitions |
+ ListOfUnitDefinitions |
+ |
+
+
+
+
+
+### Allowed children
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## ListOfUnitDefinitions
+A listOfUnitDefinitions
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+
+### Allowed children
+
+
+ listOfUnitDefinitions |
+ unitDefinition |
+ the actual list Of Unit Definitions |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## unitDefinition
+A unit definition
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ id |
+ str |
+ |
+
+
+
+
+ listOfUnits |
+ ListOfUnits |
+ List of units used to compose the definition |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## ListOfUnits
+A listOfUnits
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+
+### Allowed children
+
+
+ listOfUnits |
+ unit |
+ the actual list Of Units |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## unit
+A unit used to compose a unit definition. unit = (multiplier x 10^scale x kind)^exponent
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ kind |
+ str |
+ base unit (base or derived SI units only, see Table 2 of the SBML spec) |
+
+
+
+
+ exponent |
+ str |
+ double |
+
+
+
+
+ scale |
+ str |
+ integer |
+
+
+
+
+ multiplier |
+ str |
+ double |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## FunctionDefinition
+A function definition using MathML
+
+### Allowed parameters
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ math |
+ Math |
+ MathML function definition optional |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Math
+Subset of MathML 2.0 used to define all formulae in SBML
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ |
+
+
+
+
+ content |
+ str |
+ |
+
+
+
+
+
+## Compartment
+A compartment
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ constant |
+ bool |
+ whether size is fixed |
+
+
+
+
+ spatialDimensions |
+ float |
+ eg 3 for three dimensional space etc |
+
+
+
+
+ size |
+ float |
+ initial size of compartment |
+
+
+
+
+ units |
+ str |
+ units being used to define the compartment's size |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Species
+A species: entities of the same kind participating in reactions within a specific compartment
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ compartment |
+ str |
+ SIdRef |
+
+
+
+
+ hasOnlySubstanceUnits |
+ bool |
+ boolean |
+
+
+
+
+ boundaryCondition |
+ bool |
+ boolean |
+
+
+
+
+ constant |
+ bool |
+ boolean |
+
+
+
+
+ initialAmount |
+ float |
+ double optional |
+
+
+
+
+ initialConcentration |
+ float |
+ double optional |
+
+
+
+
+ substanceUnits |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+ conversionFactor |
+ str |
+ SIdRef optional |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Parameter
+A parameter
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ constant |
+ bool |
+ boolean |
+
+
+
+
+ value |
+ float |
+ double optional |
+
+
+
+
+ units |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## InitialAssignment
+An initial assignment
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ symbol |
+ str |
+ SIdRef required |
+
+
+
+
+ math |
+ str |
+ MathML optional |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Rule
+A rule, either algebraic, assignment or rate
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ math |
+ str |
+ MathML optional |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Constraint
+A model constraint
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ math |
+ str |
+ MathML optional |
+
+
+
+
+ message |
+ str |
+ XHTML 1.0 optional |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Reaction
+A model reaction
+
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ reversible |
+ bool |
+ boolean |
+
+
+
+
+ compartment |
+ str |
+ SIdRef optional |
+
+
+
+
+ kineticLaw |
+ KineticLaw |
+ |
+
+
+
+
+
+### Allowed children
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## KineticLaw
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ math |
+ str |
+ |
+
+
+
+
+
+### Allowed children
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## LocalParameter
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ value |
+ float |
+ |
+
+
+
+
+ units |
+ str |
+ UnitSIdRef optional |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## SpeciesReference
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ species |
+ str |
+ SIdRef |
+
+
+
+
+ stoichiometry |
+ float |
+ double optional |
+
+
+
+
+ constant |
+ bool |
+ boolean |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## SpeciesReference
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ species |
+ str |
+ SIdRef |
+
+
+
+
+ stoichiometry |
+ float |
+ double optional |
+
+
+
+
+ constant |
+ bool |
+ boolean |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## ModifierSpeciesReference
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ species |
+ str |
+ SIdRef |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Event
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ useValuesFromTriggerTime |
+ bool |
+ |
+
+
+
+
+ trigger |
+ Trigger |
+ |
+
+
+
+
+ priority |
+ Priority |
+ |
+
+
+
+
+ delay |
+ Delay |
+ |
+
+
+
+
+
+### Allowed children
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Trigger
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ initialValue |
+ bool |
+ |
+
+
+
+
+ persistent |
+ bool |
+ |
+
+
+
+
+ math |
+ str |
+ |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Priority
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ math |
+ str |
+ |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## Delay
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ math |
+ str |
+ |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
+## EventAssignment
+### Allowed parameters
+
+
+ sid |
+ str |
+ SId optional |
+
+
+
+
+ name |
+ str |
+ string optional |
+
+
+
+
+ metaid |
+ str |
+ XML ID optional |
+
+
+
+
+ sboTerm |
+ str |
+ SBOTerm optional |
+
+
+
+
+ notes |
+ Notes |
+ XHTML 1.0 optional |
+
+
+
+
+ annotation |
+ str |
+ XML content optional |
+
+
+
+
+ math |
+ str |
+ |
+
+
+
+
+ variable |
+ str |
+ SIdRef |
+
+
+
+
+
+## Notes
+XHTML field of SBase
+
+### Allowed parameters
+
+
+ xmlns |
+ str |
+ str fixed "http://www.w3.org/1999/xhtml" |
+
+
+
+
+ content |
+ str |
+ str valid XHTML |
+
+
+
+
+
diff --git a/examples/sbml/SBML.rst b/examples/sbml/SBML.rst
new file mode 100644
index 00000000..5d446b71
--- /dev/null
+++ b/examples/sbml/SBML.rst
@@ -0,0 +1,885 @@
+====
+sbml
+====
+The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf section 4.
+http://www.sbml.org/sbml/level3/version2/
+
+**Allowed parameters**
+
+=============== ======================================= ================================================================
+Allowed field Data Type Description
+=============== ======================================= ================================================================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**id** str
+**xmlns** str string, fixed to "http://www.sbml.org/sbml/level3/version2/core"
+**level** str SBML level, fixed to 3
+**version** str SBML version, fixed to 2
+**model** ` <#model>`__ Optional model
+=============== ======================================= ================================================================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+=====
+Model
+=====
+The model
+
+**Allowed parameters**
+
+========================= ======================================================================= ====================
+Allowed field Data Type Description
+========================= ======================================================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**substanceUnits** str UnitSIdRef optional
+**timeUnits** str UnitSIdRef optional
+**volumeUnits** str UnitSIdRef optional
+**areaUnits** str UnitSIdRef optional
+**lengthUnits** str UnitSIdRef optional
+**extentUnits** str UnitSIdRef optional
+**conversionFactor** str SIdRef optional
+**listOfUnitDefinitions** ` <#listofunitdefinitions>`__
+========================= ======================================================================= ====================
+
+**Allowed children**
+
+============================= ============================================ =============
+Allowed child Data Type Description
+============================= ============================================ =============
+**listOfFunctionDefinitions** `FunctionDefinition <#functiondefinition>`__
+**listOfCompartments** `Compartment <#compartment>`__
+**listOfSpecies** `Species <#species>`__
+**listOfParameters** `Parameter <#parameter>`__
+**listOfInitialAssignments** `InitialAssignment <#initialassignment>`__
+**listOfRules** `Rule <#rule>`__
+**listOfConstraints** `Constraint <#constraint>`__
+**listOfReactions** `Reaction <#reaction>`__
+**listOfEvents** `Event <#event>`__
+============================= ============================================ =============
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+=====================
+ListOfUnitDefinitions
+=====================
+A listOfUnitDefinitions
+
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+=============== ======================================= ====================
+
+**Allowed children**
+
+========================= ==================================== ===================================
+Allowed child Data Type Description
+========================= ==================================== ===================================
+**listOfUnitDefinitions** `unitDefinition <#unitdefinition>`__ the actual list Of Unit Definitions
+========================= ==================================== ===================================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+==============
+unitDefinition
+==============
+A unit definition
+
+**Allowed parameters**
+
+=============== =================================================== ============================================
+Allowed field Data Type Description
+=============== =================================================== ============================================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**id** str
+**listOfUnits** ` <#listofunits>`__ List of units used to compose the definition
+=============== =================================================== ============================================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+===========
+ListOfUnits
+===========
+A listOfUnits
+
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+=============== ======================================= ====================
+
+**Allowed children**
+
+=============== ================ ========================
+Allowed child Data Type Description
+=============== ================ ========================
+**listOfUnits** `unit <#unit>`__ the actual list Of Units
+=============== ================ ========================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+====
+unit
+====
+A unit used to compose a unit definition. unit = (multiplier x 10^scale x kind)^exponent
+
+**Allowed parameters**
+
+=============== ======================================= =======================================================================
+Allowed field Data Type Description
+=============== ======================================= =======================================================================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**kind** str base unit (base or derived SI units only, see Table 2 of the SBML spec)
+**exponent** str double
+**scale** str integer
+**multiplier** str double
+=============== ======================================= =======================================================================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+==================
+FunctionDefinition
+==================
+A function definition using MathML
+
+**Allowed parameters**
+
+=============== ======================================= ===================================
+Allowed field Data Type Description
+=============== ======================================= ===================================
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**sid** str SId optional
+**math** ` <#math>`__ MathML function definition optional
+=============== ======================================= ===================================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+====
+Math
+====
+Subset of MathML 2.0 used to define all formulae in SBML
+
+**Allowed parameters**
+
+=============== =========== =============
+Allowed field Data Type Description
+=============== =========== =============
+**xmlns** str
+**content** str
+=============== =========== =============
+
+===========
+Compartment
+===========
+A compartment
+
+**Allowed parameters**
+
+===================== ======================================= =================================================
+Allowed field Data Type Description
+===================== ======================================= =================================================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**constant** bool whether size is fixed
+**spatialDimensions** float eg 3 for three dimensional space etc
+**size** float initial size of compartment
+**units** str units being used to define the compartment's size
+===================== ======================================= =================================================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+=======
+Species
+=======
+A species: entities of the same kind participating in reactions within a specific compartment
+
+**Allowed parameters**
+
+========================= ======================================= ====================
+Allowed field Data Type Description
+========================= ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**compartment** str SIdRef
+**hasOnlySubstanceUnits** bool boolean
+**boundaryCondition** bool boolean
+**constant** bool boolean
+**initialAmount** float double optional
+**initialConcentration** float double optional
+**substanceUnits** str UnitSIdRef optional
+**conversionFactor** str SIdRef optional
+========================= ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+=========
+Parameter
+=========
+A parameter
+
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**constant** bool boolean
+**value** float double optional
+**units** str UnitSIdRef optional
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+=================
+InitialAssignment
+=================
+An initial assignment
+
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**symbol** str SIdRef required
+**math** str MathML optional
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+====
+Rule
+====
+A rule, either algebraic, assignment or rate
+
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**math** str MathML optional
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+==========
+Constraint
+==========
+A model constraint
+
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**math** str MathML optional
+**message** str XHTML 1.0 optional
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+========
+Reaction
+========
+A model reaction
+
+**Allowed parameters**
+
+=============== ================================================= ====================
+Allowed field Data Type Description
+=============== ================================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**reversible** bool boolean
+**compartment** str SIdRef optional
+**kineticLaw** ` <#kineticlaw>`__
+=============== ================================================= ====================
+
+**Allowed children**
+
+=================== ======================================================== =============
+Allowed child Data Type Description
+=================== ======================================================== =============
+**listOfReactants** `SpeciesReference <#speciesreference>`__
+**listOfProducts** `SpeciesReference <#speciesreference>`__
+**listOfModifiers** `ModifierSpeciesReference <#modifierspeciesreference>`__
+=================== ======================================================== =============
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+==========
+KineticLaw
+==========
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**math** str
+=============== ======================================= ====================
+
+**Allowed children**
+
+========================= ==================================== =============
+Allowed child Data Type Description
+========================= ==================================== =============
+**listOfLocalParameters** `LocalParameter <#localparameter>`__
+========================= ==================================== =============
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+==============
+LocalParameter
+==============
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**value** float
+**units** str UnitSIdRef optional
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+================
+SpeciesReference
+================
+**Allowed parameters**
+
+================= ======================================= ====================
+Allowed field Data Type Description
+================= ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**species** str SIdRef
+**stoichiometry** float double optional
+**constant** bool boolean
+================= ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+================
+SpeciesReference
+================
+**Allowed parameters**
+
+================= ======================================= ====================
+Allowed field Data Type Description
+================= ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**species** str SIdRef
+**stoichiometry** float double optional
+**constant** bool boolean
+================= ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+========================
+ModifierSpeciesReference
+========================
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**species** str SIdRef
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+=====
+Event
+=====
+**Allowed parameters**
+
+============================ ============================================= ====================
+Allowed field Data Type Description
+============================ ============================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**useValuesFromTriggerTime** bool
+**trigger** ` <#trigger>`__
+**priority** ` <#priority>`__
+**delay** ` <#delay>`__
+============================ ============================================= ====================
+
+**Allowed children**
+
+========================== ====================================== =============
+Allowed child Data Type Description
+========================== ====================================== =============
+**listOfEventAssignments** `EventAssignment <#eventassignment>`__
+========================== ====================================== =============
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+=======
+Trigger
+=======
+**Allowed parameters**
+
+================ ======================================= ====================
+Allowed field Data Type Description
+================ ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**initialValue** bool
+**persistent** bool
+**math** str
+================ ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+========
+Priority
+========
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**math** str
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+=====
+Delay
+=====
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**math** str
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
+===============
+EventAssignment
+===============
+**Allowed parameters**
+
+=============== ======================================= ====================
+Allowed field Data Type Description
+=============== ======================================= ====================
+**sid** str SId optional
+**name** str string optional
+**metaid** str XML ID optional
+**sboTerm** str SBOTerm optional
+**notes** ` <#notes>`__ XHTML 1.0 optional
+**annotation** str XML content optional
+**math** str
+**variable** str SIdRef
+=============== ======================================= ====================
+
+=====
+Notes
+=====
+XHTML field of SBase
+
+**Allowed parameters**
+
+=============== =========== ========================================
+Allowed field Data Type Description
+=============== =========== ========================================
+**xmlns** str str fixed "http://www.w3.org/1999/xhtml"
+**content** str str valid XHTML
+=============== =========== ========================================
+
diff --git a/examples/sbml/SBML.specification.json b/examples/sbml/SBML.specification.json
new file mode 100644
index 00000000..459a2c71
--- /dev/null
+++ b/examples/sbml/SBML.specification.json
@@ -0,0 +1,1006 @@
+{
+ "sbml": {
+ "definition": "The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf section 4.\nhttp://www.sbml.org/sbml/level3/version2/",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "id": {
+ "type": "str",
+ "description": ""
+ },
+ "xmlns": {
+ "type": "str",
+ "description": " string, fixed to \"http://www.sbml.org/sbml/level3/version2/core\""
+ },
+ "level": {
+ "type": "str",
+ "description": " SBML level, fixed to 3"
+ },
+ "version": {
+ "type": "str",
+ "description": "SBML version, fixed to 2"
+ },
+ "model": {
+ "type": "Model",
+ "description": " Optional model"
+ }
+ }
+ },
+ "Notes": {
+ "definition": "XHTML field of SBase",
+ "allowed_parameters": {
+ "xmlns": {
+ "type": "str",
+ "description": "str fixed \"http://www.w3.org/1999/xhtml\""
+ },
+ "content": {
+ "type": "str",
+ "description": "str valid XHTML"
+ }
+ }
+ },
+ "Model": {
+ "definition": "The model",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "substanceUnits": {
+ "type": "str",
+ "description": " UnitSIdRef optional"
+ },
+ "timeUnits": {
+ "type": "str",
+ "description": " UnitSIdRef optional"
+ },
+ "volumeUnits": {
+ "type": "str",
+ "description": " UnitSIdRef optional"
+ },
+ "areaUnits": {
+ "type": "str",
+ "description": " UnitSIdRef optional"
+ },
+ "lengthUnits": {
+ "type": "str",
+ "description": " UnitSIdRef optional"
+ },
+ "extentUnits": {
+ "type": "str",
+ "description": " UnitSIdRef optional"
+ },
+ "conversionFactor": {
+ "type": "str",
+ "description": "SIdRef optional"
+ },
+ "listOfUnitDefinitions": {
+ "type": "ListOfUnitDefinitions",
+ "description": ""
+ }
+ },
+ "allowed_children": {
+ "listOfFunctionDefinitions": {
+ "type": "FunctionDefinition",
+ "description": ""
+ },
+ "listOfCompartments": {
+ "type": "Compartment",
+ "description": ""
+ },
+ "listOfSpecies": {
+ "type": "Species",
+ "description": ""
+ },
+ "listOfParameters": {
+ "type": "Parameter",
+ "description": ""
+ },
+ "listOfInitialAssignments": {
+ "type": "InitialAssignment",
+ "description": ""
+ },
+ "listOfRules": {
+ "type": "Rule",
+ "description": ""
+ },
+ "listOfConstraints": {
+ "type": "Constraint",
+ "description": ""
+ },
+ "listOfReactions": {
+ "type": "Reaction",
+ "description": ""
+ },
+ "listOfEvents": {
+ "type": "Event",
+ "description": ""
+ }
+ }
+ },
+ "ListOfUnitDefinitions": {
+ "definition": "A listOfUnitDefinitions",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ }
+ },
+ "allowed_children": {
+ "listOfUnitDefinitions": {
+ "type": "unitDefinition",
+ "description": "the actual list Of Unit Definitions"
+ }
+ }
+ },
+ "unitDefinition": {
+ "definition": "A unit definition",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "id": {
+ "type": "str",
+ "description": ""
+ },
+ "listOfUnits": {
+ "type": "ListOfUnits",
+ "description": "List of units used to compose the definition"
+ }
+ }
+ },
+ "ListOfUnits": {
+ "definition": "A listOfUnits",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ }
+ },
+ "allowed_children": {
+ "listOfUnits": {
+ "type": "unit",
+ "description": "the actual list Of Units"
+ }
+ }
+ },
+ "unit": {
+ "definition": "A unit used to compose a unit definition. unit = (multiplier x 10^scale x kind)^exponent",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "kind": {
+ "type": "str",
+ "description": "base unit (base or derived SI units only, see Table 2 of the SBML spec)"
+ },
+ "exponent": {
+ "type": "str",
+ "description": "double"
+ },
+ "scale": {
+ "type": "str",
+ "description": "integer"
+ },
+ "multiplier": {
+ "type": "str",
+ "description": "double"
+ }
+ }
+ },
+ "FunctionDefinition": {
+ "definition": "A function definition using MathML",
+ "allowed_parameters": {
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "math": {
+ "type": "Math",
+ "description": "MathML function definition optional"
+ }
+ }
+ },
+ "Math": {
+ "definition": "Subset of MathML 2.0 used to define all formulae in SBML",
+ "allowed_parameters": {
+ "xmlns": {
+ "type": "str",
+ "description": ""
+ },
+ "content": {
+ "type": "str",
+ "description": ""
+ }
+ }
+ },
+ "Compartment": {
+ "definition": "A compartment",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "constant": {
+ "type": "bool",
+ "description": "whether size is fixed"
+ },
+ "spatialDimensions": {
+ "type": "float",
+ "description": "eg 3 for three dimensional space etc"
+ },
+ "size": {
+ "type": "float",
+ "description": "initial size of compartment"
+ },
+ "units": {
+ "type": "str",
+ "description": "units being used to define the compartment's size"
+ }
+ }
+ },
+ "Species": {
+ "definition": "A species: entities of the same kind participating in reactions within a specific compartment",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "compartment": {
+ "type": "str",
+ "description": "SIdRef"
+ },
+ "hasOnlySubstanceUnits": {
+ "type": "bool",
+ "description": "boolean"
+ },
+ "boundaryCondition": {
+ "type": "bool",
+ "description": "boolean"
+ },
+ "constant": {
+ "type": "bool",
+ "description": "boolean"
+ },
+ "initialAmount": {
+ "type": "float",
+ "description": "double optional"
+ },
+ "initialConcentration": {
+ "type": "float",
+ "description": "double optional"
+ },
+ "substanceUnits": {
+ "type": "str",
+ "description": "UnitSIdRef optional"
+ },
+ "conversionFactor": {
+ "type": "str",
+ "description": "SIdRef optional"
+ }
+ }
+ },
+ "Parameter": {
+ "definition": "A parameter",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "constant": {
+ "type": "bool",
+ "description": "boolean"
+ },
+ "value": {
+ "type": "float",
+ "description": "double optional"
+ },
+ "units": {
+ "type": "str",
+ "description": "UnitSIdRef optional"
+ }
+ }
+ },
+ "InitialAssignment": {
+ "definition": "An initial assignment",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "symbol": {
+ "type": "str",
+ "description": "SIdRef required"
+ },
+ "math": {
+ "type": "str",
+ "description": "MathML optional"
+ }
+ }
+ },
+ "Rule": {
+ "definition": "A rule, either algebraic, assignment or rate",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "math": {
+ "type": "str",
+ "description": "MathML optional"
+ }
+ }
+ },
+ "Constraint": {
+ "definition": "A model constraint",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "math": {
+ "type": "str",
+ "description": " MathML optional"
+ },
+ "message": {
+ "type": "str",
+ "description": "XHTML 1.0 optional"
+ }
+ }
+ },
+ "Reaction": {
+ "definition": "A model reaction",
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "reversible": {
+ "type": "bool",
+ "description": "boolean"
+ },
+ "compartment": {
+ "type": "str",
+ "description": "SIdRef optional"
+ },
+ "kineticLaw": {
+ "type": "KineticLaw",
+ "description": ""
+ }
+ },
+ "allowed_children": {
+ "listOfReactants": {
+ "type": "SpeciesReference",
+ "description": ""
+ },
+ "listOfProducts": {
+ "type": "SpeciesReference",
+ "description": ""
+ },
+ "listOfModifiers": {
+ "type": "ModifierSpeciesReference",
+ "description": ""
+ }
+ }
+ },
+ "KineticLaw": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "math": {
+ "type": "str",
+ "description": ""
+ }
+ },
+ "allowed_children": {
+ "listOfLocalParameters": {
+ "type": "LocalParameter",
+ "description": ""
+ }
+ }
+ },
+ "LocalParameter": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "value": {
+ "type": "float",
+ "description": ""
+ },
+ "units": {
+ "type": "str",
+ "description": "UnitSIdRef optional"
+ }
+ }
+ },
+ "SpeciesReference": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "species": {
+ "type": "str",
+ "description": "SIdRef"
+ },
+ "stoichiometry": {
+ "type": "float",
+ "description": "double optional"
+ },
+ "constant": {
+ "type": "bool",
+ "description": "boolean"
+ }
+ }
+ },
+ "ModifierSpeciesReference": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "species": {
+ "type": "str",
+ "description": "SIdRef"
+ }
+ }
+ },
+ "Event": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "useValuesFromTriggerTime": {
+ "type": "bool",
+ "description": ""
+ },
+ "trigger": {
+ "type": "Trigger",
+ "description": ""
+ },
+ "priority": {
+ "type": "Priority",
+ "description": ""
+ },
+ "delay": {
+ "type": "Delay",
+ "description": ""
+ }
+ },
+ "allowed_children": {
+ "listOfEventAssignments": {
+ "type": "EventAssignment",
+ "description": ""
+ }
+ }
+ },
+ "Trigger": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "initialValue": {
+ "type": "bool",
+ "description": ""
+ },
+ "persistent": {
+ "type": "bool",
+ "description": ""
+ },
+ "math": {
+ "type": "str",
+ "description": ""
+ }
+ }
+ },
+ "Priority": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "math": {
+ "type": "str",
+ "description": ""
+ }
+ }
+ },
+ "Delay": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "math": {
+ "type": "str",
+ "description": ""
+ }
+ }
+ },
+ "EventAssignment": {
+ "allowed_parameters": {
+ "sid": {
+ "type": "str",
+ "description": " SId optional"
+ },
+ "name": {
+ "type": "str",
+ "description": " string optional"
+ },
+ "metaid": {
+ "type": "str",
+ "description": " XML ID optional"
+ },
+ "sboTerm": {
+ "type": "str",
+ "description": "SBOTerm optional"
+ },
+ "notes": {
+ "type": "Notes",
+ "description": " XHTML 1.0 optional"
+ },
+ "annotation": {
+ "type": "str",
+ "description": "XML content optional"
+ },
+ "math": {
+ "type": "str",
+ "description": ""
+ },
+ "variable": {
+ "type": "str",
+ "description": "SIdRef"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/sbml/SBML.specification.yaml b/examples/sbml/SBML.specification.yaml
new file mode 100644
index 00000000..0a420b31
--- /dev/null
+++ b/examples/sbml/SBML.specification.yaml
@@ -0,0 +1,735 @@
+sbml:
+ definition: 'The top-level SBML container implementing SBML 3.2. See sbml.level-3.version-2.core.release-2.pdf
+ section 4.
+
+ http://www.sbml.org/sbml/level3/version2/'
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ id:
+ type: str
+ description: ''
+ xmlns:
+ type: str
+ description: ' string, fixed to "http://www.sbml.org/sbml/level3/version2/core"'
+ level:
+ type: str
+ description: ' SBML level, fixed to 3'
+ version:
+ type: str
+ description: SBML version, fixed to 2
+ model:
+ type: Model
+ description: ' Optional model'
+Notes:
+ definition: XHTML field of SBase
+ allowed_parameters:
+ xmlns:
+ type: str
+ description: str fixed "http://www.w3.org/1999/xhtml"
+ content:
+ type: str
+ description: str valid XHTML
+Model:
+ definition: The model
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ substanceUnits:
+ type: str
+ description: ' UnitSIdRef optional'
+ timeUnits:
+ type: str
+ description: ' UnitSIdRef optional'
+ volumeUnits:
+ type: str
+ description: ' UnitSIdRef optional'
+ areaUnits:
+ type: str
+ description: ' UnitSIdRef optional'
+ lengthUnits:
+ type: str
+ description: ' UnitSIdRef optional'
+ extentUnits:
+ type: str
+ description: ' UnitSIdRef optional'
+ conversionFactor:
+ type: str
+ description: SIdRef optional
+ listOfUnitDefinitions:
+ type: ListOfUnitDefinitions
+ description: ''
+ allowed_children:
+ listOfFunctionDefinitions:
+ type: FunctionDefinition
+ description: ''
+ listOfCompartments:
+ type: Compartment
+ description: ''
+ listOfSpecies:
+ type: Species
+ description: ''
+ listOfParameters:
+ type: Parameter
+ description: ''
+ listOfInitialAssignments:
+ type: InitialAssignment
+ description: ''
+ listOfRules:
+ type: Rule
+ description: ''
+ listOfConstraints:
+ type: Constraint
+ description: ''
+ listOfReactions:
+ type: Reaction
+ description: ''
+ listOfEvents:
+ type: Event
+ description: ''
+ListOfUnitDefinitions:
+ definition: A listOfUnitDefinitions
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ allowed_children:
+ listOfUnitDefinitions:
+ type: unitDefinition
+ description: the actual list Of Unit Definitions
+unitDefinition:
+ definition: A unit definition
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ id:
+ type: str
+ description: ''
+ listOfUnits:
+ type: ListOfUnits
+ description: List of units used to compose the definition
+ListOfUnits:
+ definition: A listOfUnits
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ allowed_children:
+ listOfUnits:
+ type: unit
+ description: the actual list Of Units
+unit:
+ definition: A unit used to compose a unit definition. unit = (multiplier x 10^scale
+ x kind)^exponent
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ kind:
+ type: str
+ description: base unit (base or derived SI units only, see Table 2 of
+ the SBML spec)
+ exponent:
+ type: str
+ description: double
+ scale:
+ type: str
+ description: integer
+ multiplier:
+ type: str
+ description: double
+FunctionDefinition:
+ definition: A function definition using MathML
+ allowed_parameters:
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ sid:
+ type: str
+ description: ' SId optional'
+ math:
+ type: Math
+ description: MathML function definition optional
+Math:
+ definition: Subset of MathML 2.0 used to define all formulae in SBML
+ allowed_parameters:
+ xmlns:
+ type: str
+ description: ''
+ content:
+ type: str
+ description: ''
+Compartment:
+ definition: A compartment
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ constant:
+ type: bool
+ description: whether size is fixed
+ spatialDimensions:
+ type: float
+ description: eg 3 for three dimensional space etc
+ size:
+ type: float
+ description: initial size of compartment
+ units:
+ type: str
+ description: units being used to define the compartment's size
+Species:
+ definition: 'A species: entities of the same kind participating in reactions within
+ a specific compartment'
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ compartment:
+ type: str
+ description: SIdRef
+ hasOnlySubstanceUnits:
+ type: bool
+ description: boolean
+ boundaryCondition:
+ type: bool
+ description: boolean
+ constant:
+ type: bool
+ description: boolean
+ initialAmount:
+ type: float
+ description: double optional
+ initialConcentration:
+ type: float
+ description: double optional
+ substanceUnits:
+ type: str
+ description: UnitSIdRef optional
+ conversionFactor:
+ type: str
+ description: SIdRef optional
+Parameter:
+ definition: A parameter
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ constant:
+ type: bool
+ description: boolean
+ value:
+ type: float
+ description: double optional
+ units:
+ type: str
+ description: UnitSIdRef optional
+InitialAssignment:
+ definition: An initial assignment
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ symbol:
+ type: str
+ description: SIdRef required
+ math:
+ type: str
+ description: MathML optional
+Rule:
+ definition: A rule, either algebraic, assignment or rate
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ math:
+ type: str
+ description: MathML optional
+Constraint:
+ definition: A model constraint
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ math:
+ type: str
+ description: ' MathML optional'
+ message:
+ type: str
+ description: XHTML 1.0 optional
+Reaction:
+ definition: A model reaction
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ reversible:
+ type: bool
+ description: boolean
+ compartment:
+ type: str
+ description: SIdRef optional
+ kineticLaw:
+ type: KineticLaw
+ description: ''
+ allowed_children:
+ listOfReactants:
+ type: SpeciesReference
+ description: ''
+ listOfProducts:
+ type: SpeciesReference
+ description: ''
+ listOfModifiers:
+ type: ModifierSpeciesReference
+ description: ''
+KineticLaw:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ math:
+ type: str
+ description: ''
+ allowed_children:
+ listOfLocalParameters:
+ type: LocalParameter
+ description: ''
+LocalParameter:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ value:
+ type: float
+ description: ''
+ units:
+ type: str
+ description: UnitSIdRef optional
+SpeciesReference:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ species:
+ type: str
+ description: SIdRef
+ stoichiometry:
+ type: float
+ description: double optional
+ constant:
+ type: bool
+ description: boolean
+ModifierSpeciesReference:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ species:
+ type: str
+ description: SIdRef
+Event:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ useValuesFromTriggerTime:
+ type: bool
+ description: ''
+ trigger:
+ type: Trigger
+ description: ''
+ priority:
+ type: Priority
+ description: ''
+ delay:
+ type: Delay
+ description: ''
+ allowed_children:
+ listOfEventAssignments:
+ type: EventAssignment
+ description: ''
+Trigger:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ initialValue:
+ type: bool
+ description: ''
+ persistent:
+ type: bool
+ description: ''
+ math:
+ type: str
+ description: ''
+Priority:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ math:
+ type: str
+ description: ''
+Delay:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ math:
+ type: str
+ description: ''
+EventAssignment:
+ allowed_parameters:
+ sid:
+ type: str
+ description: ' SId optional'
+ name:
+ type: str
+ description: ' string optional'
+ metaid:
+ type: str
+ description: ' XML ID optional'
+ sboTerm:
+ type: str
+ description: SBOTerm optional
+ notes:
+ type: Notes
+ description: ' XHTML 1.0 optional'
+ annotation:
+ type: str
+ description: XML content optional
+ math:
+ type: str
+ description: ''
+ variable:
+ type: str
+ description: SIdRef
diff --git a/examples/sbml/test_sbml3.py b/examples/sbml/test_sbml3.py
index 50f595a3..6b1c717b 100755
--- a/examples/sbml/test_sbml3.py
+++ b/examples/sbml/test_sbml3.py
@@ -41,6 +41,31 @@ def test_example_sbml_minimal():
sbml_doc.to_yaml_file(f"{name}.yaml")
sbml_doc.to_xml_file(f"{name}.xml")
+ print("Generating documentation...")
+
+ doc_md = sbml_doc.generate_documentation(format="markdown")
+
+ with open("SBML.md", "w") as d:
+ d.write(doc_md)
+
+ doc_rst = sbml_doc.generate_documentation(format="rst")
+
+ with open("SBML.rst", "w") as d:
+ d.write(doc_rst)
+
+ print("\n >> Generating specification in dict form...")
+ doc_dict = sbml_doc.generate_documentation(format="dict")
+
+ with open("SBML.specification.json", "w") as d:
+ d.write(json.dumps(doc_dict, indent=4))
+
+ print(" >> Generating specification in YAML...\n")
+
+ with open("SBML.specification.yaml", "w") as d:
+ yy = yaml.dump(doc_dict, indent=4, sort_keys=False)
+ # print(yy)
+ d.write(yy)
+
if __name__ == "__main__":
test_example_sbml_minimal()
From 37a9da1d6def92dab378210ee1245ebceccbd830 Mon Sep 17 00:00:00 2001
From: pgleeson
Date: Tue, 19 Sep 2023 10:53:27 +0100
Subject: [PATCH 21/32] Update COMBINE specs docs
---
examples/COMBINE.md | 15 ++++++++++++---
examples/images/combine.png | Bin 0 -> 168517 bytes
examples/images/neuroml.png | Bin 0 -> 3889 bytes
examples/images/sbml.png | Bin 0 -> 18373 bytes
examples/sbml/SBML.md | 1 -
examples/sbml/SBML.rst | 1 -
examples/sbml/SBML.specification.json | 2 +-
7 files changed, 13 insertions(+), 6 deletions(-)
create mode 100644 examples/images/combine.png
create mode 100644 examples/images/neuroml.png
create mode 100644 examples/images/sbml.png
diff --git a/examples/COMBINE.md b/examples/COMBINE.md
index 72e6ad4e..3aee77c5 100644
--- a/examples/COMBINE.md
+++ b/examples/COMBINE.md
@@ -1,14 +1,24 @@
# COMBINE standards in modelspec
+![combine](images/combine.png)
+
To illustrate the utility of modelspec, and to create a uniform, compatible set of APIs across a diverse range of model specification formats, we are investigating the [COMBINE set of standards in computational biology](https://co.mbine.org/standards/), and attempting to express these formats in modelspec.
## NeuroML
-A preliminary version of an API for [NeuroML2](https://docs.neuroml.org/Userdocs/NeuroMLv2.html) can be found [here](NeuroML).
+![neuroml](images/neuroml.png)
+
+A preliminary version of an API for [NeuroML2](https://docs.neuroml.org/Userdocs/NeuroMLv2.html) can be found [here](neuroml2). This allows generation of NeuroML2 models using the modelspec based API, saving as valid XML, as well as JSON, YAML and BSON equivalents. See [neuroml2_spec.py](neuroml2/neuroml2_spec.py).
+
+[NeuroMLlite](https://github.com/NeuroML/NeuroMLlite) already uses modelspec as its primary specification format. See [here](https://github.com/NeuroML/NeuroMLlite/blob/master/neuromllite/__init__.py).
## SBML
-work towards an API for [SBML](https://www.sbml.org/) can be found [here](SBML).
+![combine](images/sbml.png)
+
+Work towards an API for [SBML](https://www.sbml.org/) can be found [here](sbml).
+
+An example of an SBML file generated from this is [here](sbml/test_minimal_example.xml).
## CellML
@@ -17,4 +27,3 @@ TODO...
## SED-ML
TODO...
-
diff --git a/examples/images/combine.png b/examples/images/combine.png
new file mode 100644
index 0000000000000000000000000000000000000000..3ba0e32d9bccf9ee34c7f4fec6afb38121552941
GIT binary patch
literal 168517
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