Skip to content

Commit

Permalink
Merge pull request #305 from robertvi/feat/-run-tellurium
Browse files Browse the repository at this point in the history
Feat/ run tellurium
  • Loading branch information
pgleeson authored Feb 16, 2024
2 parents 5f8d1bd + d208bb0 commit 522167c
Show file tree
Hide file tree
Showing 12 changed files with 666 additions and 6 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,5 @@ arm64
/tests/analysis/*png
*ken.sh
/tests/plot/test_morphology*.png

.vscode
9 changes: 9 additions & 0 deletions examples/test_data/valid_doc_sedml.sbml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created by libAntimony version v2.8.1 on 2015-12-02 12:55 with libSBML version 5.11.9. -->
<sbml xmlns="http://www.sbml.org/sbml/level3/version1/core" xmlns:comp="http://www.sbml.org/sbml/level3/version1/comp/version1" level="3" version="1" comp:required="true">
<model id="__main" name="__main">
<listOfParameters>
<parameter id="a" value="3" constant="true"/>
</listOfParameters>
</model>
</sbml>
41 changes: 41 additions & 0 deletions examples/test_data/valid_doc_sedml.sedml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Created by phraSED-ML version v0.5beta on 2015-12-01 15:44 with libSBML version 5.11.9. -->
<sedML xmlns="http://sed-ml.org/sed-ml/level1/version2" level="1" version="2">
<listOfSimulations>
<uniformTimeCourse id="sim1" initialTime="0" outputStartTime="0" outputEndTime="10" numberOfPoints="10">
<algorithm kisaoID="KISAO:0000019"/>
</uniformTimeCourse>
</listOfSimulations>
<listOfModels>
<model id="mod1" language="urn:sedml:language:sbml.level-3.version-1" source="valid_doc_sedml.sbml"/>
</listOfModels>
<listOfTasks>
<task id="task1" modelReference="mod1" simulationReference="sim1"/>
</listOfTasks>
<listOfDataGenerators>
<dataGenerator id="report_0_0_0" name="time">
<listOfVariables>
<variable id="time" symbol="urn:sedml:symbol:time" taskReference="task1"/>
</listOfVariables>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<ci> time </ci>
</math>
</dataGenerator>
<dataGenerator id="report_0_0_1" name="a">
<listOfVariables>
<variable id="a" target="/sbml:sbml/sbml:model/descendant::*[@id='a']" taskReference="task1" modelReference="mod1"/>
</listOfVariables>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<ci> a </ci>
</math>
</dataGenerator>
</listOfDataGenerators>
<listOfOutputs>
<report id="report_0">
<listOfDataSets>
<dataSet id="report_0_0_0_dataset" label="time" dataReference="report_0_0_0"/>
<dataSet id="report_0_0_1_dataset" label="a" dataReference="report_0_0_1"/>
</listOfDataSets>
</report>
</listOfOutputs>
</sedML>
45 changes: 41 additions & 4 deletions pyneuroml/pynml.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ def _parse_arguments():
"input_files",
type=str,
nargs="*",
metavar="<LEMS/NeuroML 2/SBML file(s)>",
help="LEMS/NeuroML 2/SBML file(s) to process",
metavar="<LEMS/NeuroML 2/SBML/SEDML file(s)>",
help="LEMS/NeuroML 2/SBML/SEDML file(s) to process",
)

mut_exc_opts_grp = parser.add_argument_group(
Expand Down Expand Up @@ -187,7 +187,7 @@ def _parse_arguments():
"The full format of the '-neuron' option is:\n"
"-neuron [-nogui] [-run] [-outputdir dir] <LEMS file>\n"
" -nogui\n"
" do not generate gtaphical elements in NEURON,\n"
" do not generate graphical elements in NEURON,\n"
" just run, save data, and quit\n"
" -run\n"
" compile NMODL files and run the main NEURON\n"
Expand All @@ -198,6 +198,20 @@ def _parse_arguments():
" the LEMS file to use"
),
)
mut_exc_opts.add_argument(
"-run-tellurium",
nargs=argparse.REMAINDER,
help=(
"Load a SEDML file, and run it using tellurium:\n"
"<SEDML file> -run-tellurium [-outputdir dir]\n"
" <SEDML file>\n"
" the SEDML file to use\n"
" -outputdir <dir>\n"
" save any output reports in directory <dir>\n"
" default is current directory ie '.'\n"
" use 'none' to disable output altogether"
),
)
mut_exc_opts.add_argument(
"-netpyne",
nargs=argparse.REMAINDER,
Expand Down Expand Up @@ -325,7 +339,7 @@ def _parse_arguments():
nargs=3,
help=(
"(Via jNeuroML) Load a SBML file, and convert it\n"
"toLEMS format using values for duration & dt\n"
"to LEMS format using values for duration & dt\n"
"in ms (ignoring SBML units)"
),
)
Expand Down Expand Up @@ -601,6 +615,29 @@ def _evaluate_arguments(args):
logger.error("one or more SEDML files failed to validate")
sys.exit(UNKNOWN_ERR)

# Deal with the -run-tellurium option which doesn't call run_jneuroml
if args.run_tellurium is not None:
try:
from pyneuroml.tellurium import run_from_sedml_file
except Exception:
logger.critical("Unable to import pyneuroml.tellurium")
sys.exit(UNKNOWN_ERR)

if len(args.input_files) < 1:
logger.critical("No input files specified")
sys.exit(ARGUMENT_ERR)
elif len(args.input_files) != 1:
logger.critical("Only a single input file is supported by this option")
sys.exit(ARGUMENT_ERR)

try:
run_from_sedml_file(args.input_files, args.run_tellurium)
except Exception as e:
logger.critical(f"run_from_sedml_file failed with {str(e)}")
sys.exit(UNKNOWN_ERR)

sys.exit(0)

# These do not use the shared option where files are supplied
# They require the file name to be specified after
# TODO: handle these better
Expand Down
69 changes: 69 additions & 0 deletions pyneuroml/tellurium/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
run a model using the tellurium engine
"""

from pyneuroml.sedml import validate_sedml_files
import tellurium as te

te.setDefaultPlottingEngine("matplotlib")

import os
import libsedml

# # For technical reasons, any software which uses libSEDML
# # must provide a custom build - Tellurium uses tesedml
# try:
# import libsedml
# except ImportError:
# import tesedml as libsedml


def run_from_sedml_file(input_files, args):
"read a SEDML file and run it using tellurium's executeSEDML command"

# input_files list is a shared option with commands that can take >1 filename
# therefore datatype is a list not a single filename
if not len(input_files) >= 1:
raise ValueError("No input files specified")

if len(input_files) > 1:
raise ValueError("Only a single input file is supported")

# set default output directory
outputdir = "."
saveoutputs = True

if "-outputdir" in args:
try:
outputdir = args[args.index("-outputdir") + 1]
except:
raise ValueError("Incorrectly specified outputdir")

if outputdir == "none":
outputdir = None
saveoutputs = False

file_name = input_files[0]

# tellurium seems not to do much validation so we do our own here
if not validate_sedml_files([file_name]):
raise IOError(f"failed to validate SEDML file {file_name}")

try:
doc = libsedml.readSedML(file_name)
except Exception:
raise IOError(f"readSedML failed trying to open the file {file_name}")

working_dir = os.path.dirname(file_name)
if working_dir == "":
working_dir = "."
to_sed = doc.toSed()

# execute SED-ML using Tellurium
te.executeSEDML(
to_sed,
workingDir=working_dir,
createOutputs=True,
saveOutputs=saveoutputs,
outputDir=outputdir,
)
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ combine =
python-libsbml
python-libsedml

tellurium =
tellurium

all =
pyNeuroML[neuron]
pyNeuroML[brian]
Expand All @@ -119,6 +122,7 @@ all =
pyNeuroML[plotly]
pyNeuroML[nsg]
pyNeuroML[combine]
pyNeuroML[tellurium]

dev =
pyNeuroML[all]
Expand Down
15 changes: 13 additions & 2 deletions test-ghactions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,20 @@ echo
echo "################################################"
echo "## Simple SBML validation example"

pynml -validate-sbml test_data/valid_doc.sbml
pynml -validate-sbml-units test_data/valid_doc.sbml
pynml test_data/valid_doc.sbml -validate-sbml
pynml test_data/valid_doc.sbml -validate-sbml-units

echo
echo "################################################"
echo "## Simple SEDML validation example"

pynml test_data/valid_doc_sedml.sedml -validate-sedml

echo
echo "################################################"
echo "## Tellurium validation example"

pynml test_data/valid_doc_sedml.sedml -run-tellurium -outputdir none

echo
echo "################################################"
Expand Down
47 changes: 47 additions & 0 deletions tests/tellurium/do_test_tellurium.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python3

"""
tests for "pynml <sedml_file> -run-tellurium" feature
named so that pytest will not test this file
rather runs directly from test-ghactions.sh
from the examples directory
as pytest is segfaulting
"""


from pyneuroml import tellurium

import os


def test_run_tellurium_on_valid_file():
"ensure it runs a basic sedml file without error"

fname = "../tests/sedml/test_data/valid_doc.sedml"
tellurium.run_from_sedml_file([fname], args=["-outputdir", "none"])


def test_run_tellurium_pdf_output():
"ensure it outputs expected pdf file"

if os.path.exists("./d1.pdf"):
os.unlink("./d1.pdf")

fname = "../tests/tellurium/test_data/LEMS_NML2_Ex9_FN.sedml"
tellurium.run_from_sedml_file([fname], args=[])

assert os.path.exists("./d1.pdf")
os.unlink("./d1.pdf")


def test_run_hindmarsh_rose():
"test run of hindmarsh rose"

fname = "../tests/tellurium/test_data/LEMS_Regular_HindmarshRose.sedml"
tellurium.run_from_sedml_file([fname], args=["-outputdir", "none"])


if __name__ == "__main__":
test_run_tellurium_on_valid_file()
test_run_tellurium_pdf_output()
test_run_hindmarsh_rose()
98 changes: 98 additions & 0 deletions tests/tellurium/test_data/LEMS_NML2_Ex9_FN.sbml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?xml version='1.0' encoding='UTF-8'?>
<sbml xmlns="http://www.sbml.org/sbml/level2/version2" metaid="metaid_0000001" level="2" version="2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sbml.org/sbml/level2/version2 http://sbml.org/Special/xml-schemas/sbml-l2v2-schema/sbml.xsd">
<notes>
<p xmlns="http://www.w3.org/1999/xhtml">

This SBML file has been generated by org.neuroml.export (see https://github.com/NeuroML/org.neuroml.export)
org.neuroml.export v1.10.0
org.neuroml.model v1.10.0
jLEMS v0.11.0

Export of model:
Components:
fn1 (Type: fitzHughNagumoCell: I=0.8 (dimensionless) SEC=1.0 (SI time))
net1 (Type: network)
sim1 (Type: Simulation: length=200.0 (SI time) step=0.01 (SI time))

</p>
</notes>
<model id="net1" name="net1">


<!--Adding simulation Component(id=sim1 type=Simulation) of network: net1 (Type: network)-->

<listOfCompartments>

<!--Population fnPop1 contains 1 instances of components of: Component(id=fn1 type=fitzHughNagumoCell)-->

<compartment id="fnPop1_0" size="1"/>
</listOfCompartments>

<listOfParameters>
<parameter id="I" value="0.8" constant="true"/>
<parameter id="SEC" value="1.0" constant="true"/>
<parameter id="V" value="0" constant="false"/>
<parameter id="W" value="0" constant="false"/>
</listOfParameters>


<listOfRules>
<rateRule variable="V">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<apply>
<divide/>
<apply>
<plus/>
<apply>
<minus/>
<apply>
<minus/>
<ci>V</ci>
<apply>
<divide/>
<apply>
<power/>
<ci>V</ci>
<cn>3.0</cn>
</apply>
<cn>3.0</cn>
</apply>
</apply>
<ci>W</ci>
</apply>
<ci>I</ci>
</apply>
<ci>SEC</ci>
</apply>
</math>
</rateRule>
<rateRule variable="W">
<math xmlns="http://www.w3.org/1998/Math/MathML">
<apply>
<divide/>
<apply>
<times/>
<cn>0.08</cn>
<apply>
<plus/>
<ci>V</ci>
<apply>
<minus/>
<cn>0.7</cn>
<apply>
<times/>
<cn>0.8</cn>
<ci>W</ci>
</apply>
</apply>
</apply>
</apply>
<ci>SEC</ci>
</apply>
</math>
</rateRule>
</listOfRules>


</model>
</sbml>
Loading

0 comments on commit 522167c

Please sign in to comment.