forked from ASinanSaglam/BNG_BioSimDocker
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
editing repo to adhere to BioSimulations interface; simplifying code;…
… integrating in BioSimulation-utils for parsing SED-ML and COMBINE files
- Loading branch information
Showing
20 changed files
with
816 additions
and
222 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
*.pyc | ||
_pycache__/ | ||
*.egg-info/ | ||
|
||
tests/results/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
from ._version import __version__ # noqa: F401 | ||
# :obj:`str`: version | ||
|
||
from .core import exec_combine_archive # noqa: F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
""" BioSimulations-compliant command-line interface to the `BioNetGen <https://bionetgen.org/>`_ simulation program. | ||
:Author: Jonathan Karr <karr@mssm.edu> | ||
:Date: 2020-04-13 | ||
:Copyright: 2020, Center for Reproducible Biomedical Modeling | ||
:License: MIT | ||
""" | ||
|
||
from .core import exec_combine_archive | ||
import Biosimulations_bionetgen | ||
import cement | ||
|
||
|
||
class BaseController(cement.Controller): | ||
""" Base controller for command line application """ | ||
|
||
class Meta: | ||
label = 'base' | ||
description = "BioSimulations-compliant command-line interface to the BioNetGen simulation program <https://bionetgen.org>." | ||
help = "bionetgen" | ||
arguments = [ | ||
(['-i', '--archive'], dict(type=str, | ||
required=True, | ||
help='Path to OMEX file which contains one or more SED-ML-encoded simulation experiments')), | ||
(['-o', '--out-dir'], dict(type=str, | ||
default='.', | ||
help='Directory to save outputs')), | ||
(['-v', '--version'], dict(action='version', | ||
version=Biosimulations_bionetgen.__version__)), | ||
] | ||
|
||
@cement.ex(hide=True) | ||
def _default(self): | ||
args = self.app.pargs | ||
exec_combine_archive(args.archive, args.out_dir) | ||
|
||
|
||
class App(cement.App): | ||
""" Command line application """ | ||
class Meta: | ||
label = 'bionetgen' | ||
base_controller = 'base' | ||
handlers = [ | ||
BaseController, | ||
] | ||
|
||
|
||
def main(): | ||
with App() as app: | ||
app.run() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
__version__ = '2.5.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
""" BioSimulations-compliant command-line interface to the `BioNetGen <https://bionetgen.org/>`_ simulation program. | ||
:Author: Ali Sinan Saglam <als251@pitt.edu> | ||
:Author: Jonathan Karr <karr@mssm.edu> | ||
:Date: 2020-04-13 | ||
:Copyright: 2020, Center for Reproducible Biomedical Modeling | ||
:License: MIT | ||
""" | ||
|
||
from Biosimulations_utils.simulation.data_model import Simulation # noqa: F401 | ||
import Biosimulations_utils.simulator.utils.exec_simulations_in_archive | ||
import os | ||
import pandas | ||
import re | ||
import subprocess | ||
import tempfile | ||
|
||
__all__ = ['exec_combine_archive', 'BioNetGenSimulationRunner'] | ||
|
||
|
||
def exec_combine_archive(archive_file, out_dir): | ||
""" Execute the SED tasks defined in a COMBINE archive and save the outputs | ||
Args: | ||
archive_file (:obj:`str`): path to COMBINE archive | ||
out_dir (:obj:`str`): directory to store the outputs of the tasks | ||
""" | ||
Biosimulations_utils.simulator.utils.exec_simulations_in_archive(archive_file, BioNetGenSimulationRunner().run, out_dir) | ||
|
||
|
||
class BioNetGenSimulationRunner(object): | ||
def run(self, model_filename, model_sed_urn, simulation, working_dir, out_filename, out_format): | ||
""" Execute a simulation and save its results | ||
Args: | ||
model_filename (:obj:`str`): path to the model in BNGL format | ||
model_sed_urn (:obj:`str`): SED URN for the format of the model (e.g., `urn:sedml:language:sbml`) | ||
simulation (:obj:`Simulation`): simulation | ||
working_dir (:obj:`str`): directory of the SED-ML file | ||
out_filename (:obj:`str`): path to save the results of the simulation | ||
out_format (:obj:`str`): format to save the results of the simulation (e.g., `csv`) | ||
""" | ||
|
||
# read the model from the BNGL file | ||
model_lines = self.read_model(model_filename, model_sed_urn) | ||
|
||
# modify the model according to `simulation` | ||
modified_model_lines = self.modify_model(model_lines, simulation) | ||
|
||
# write the modified model lines to a BNGL file | ||
modified_model_filename = tempfile.mkstemp(suffix='.bngl') | ||
with open(modified_model_filename, "w") as file: | ||
file.writelines(modified_model_lines) | ||
|
||
# simulate the modified model | ||
subprocess.call(['BNG2.pl', modified_model_filename]) | ||
|
||
# put files into output path | ||
gdat_results_filename = modified_model_filename.replace('.bngl', '.gdat') | ||
self.convert_simulation_results(gdat_results_filename, out_filename, out_format) | ||
|
||
# cleanup temporary files | ||
os.remove(modified_model_filename) | ||
os.remove(gdat_results_filename) | ||
|
||
def read_model(self, model_filename, model_sed_urn): | ||
""" Read a model from a file | ||
Args: | ||
model_filename (:obj:`str`): path to the model in BNGL format | ||
model_sed_urn (:obj:`str`): SED URN for the format of the model (e.g., `urn:sedml:language:sbml`) | ||
Returns: | ||
:obj:`list` of :obj:`str`: model | ||
Raises: | ||
:obj:`NotImplementedError`: if the model is not in BGNL format | ||
""" | ||
if model_sed_urn != 'urn:sedml:language:bngl': | ||
raise NotImplementedError('Model format with SED URN {} is not supported'.format(model_sed_urn)) | ||
|
||
with open(model_filename, "r") as file: | ||
line = file.readline() | ||
model_lines = [] | ||
# TODO: end model is not necessary, find other | ||
# ways to check for the action block | ||
while line.strip() != "end model": | ||
model_lines.append(line) | ||
line = file.readline() | ||
model_lines.append(line) | ||
return model_lines | ||
|
||
def modify_model(self, model_lines, simulation): | ||
""" Modify a model according to a specified simulation experiment | ||
* Modify model parameters | ||
* Set simulation time course | ||
* Set simulation algorithm and algorithm parameters | ||
Args: | ||
model_lines (:obj:`list` of :obj:`str`): model | ||
simulation (:obj:`Simulation`): simulation | ||
Returns: | ||
:obj:`list` of :obj:`str`: modified model | ||
Raises: | ||
:obj:`NotImplementedError`: if the desired simulation algorithm is not supported | ||
""" | ||
|
||
# use `setParameter` to add parameter changes to the model | ||
for change in simulation.model_parameter_changes: | ||
# TODO: properly apply parameter changes | ||
model_lines += 'setParameter("{}", {})\n'.format(change.parameter.id, change.value) | ||
|
||
# get the initial time, end time, and the number of time points to record | ||
t_start = simulation.start_time | ||
t_end = simulation.end_time | ||
n_steps = simulation.num_time_points | ||
|
||
# Get algorithm parameters and use them properly | ||
alg_params = self.get_algorithm_parameters(simulation.parameters) | ||
|
||
# add the simulation to the model lines | ||
assert simulation.algorithm, "Simulation must define an algorithm" | ||
assert simulation.algorithm.kisao_term, "Simulation algorithm must include a KiSAO term" | ||
assert simulation.algorithm.kisao_term.ontology == 'KISAO', "Simulation algorithm must include a KiSAO term" | ||
|
||
if simulation.algorithm.kisao_term.id == '0000019': | ||
model_lines += "generate_network({overwrite=>1})\n" | ||
simulate_line = 'simulate({' + 'method => "{}", t_start => {}, t_end => {}, n_steps => {}'.format( | ||
"ode", t_start, t_end, n_steps) | ||
for param, val in alg_params.items(): | ||
simulate_line += ", {}=>{}".format(param, val) | ||
simulate_line += '})\n' | ||
model_lines += simulate_line | ||
|
||
elif simulation.algorithm.kisao_term.id == '0000029': | ||
model_lines += "generate_network({overwrite=>1})\n" | ||
simulate_line = 'simulate({' + 'method => "{}", t_start => {}, t_end => {}, n_steps => {}'.format( | ||
"ssa", t_start, t_end, n_steps) | ||
# TODO: support algorothm parameters | ||
simulate_line += '})\n' | ||
model_lines += simulate_line | ||
|
||
elif simulation.algorithm.kisao_term.id == '0000263': | ||
simulate_line = 'simulate({' + 'method => "{}", t_start => {}, t_end => {}, n_steps => {}'.format( | ||
"nfsim", t_start, t_end, n_steps) | ||
# TODO: support algorothm parameters | ||
simulate_line += '})\n' | ||
model_lines += simulate_line | ||
|
||
else: | ||
raise NotImplementedError("Algorithm with KiSAO id {} is not supported".format(simulation.algorithm.kisao_term.id)) | ||
|
||
# return the modified model | ||
return model_lines | ||
|
||
def get_algorithm_parameters(self, parameter_changes): | ||
""" Get the desired algorithm parameters for a simulation | ||
Args: | ||
parameter_changes (:obj:`list` of :obj:`ParameterChange`): parameter changes | ||
Returns: | ||
:obj:`dict`: dictionary that maps the ids of parameters to their values | ||
Raises: | ||
:obj:`NotImplementedError`: if desired algorithm parameter is not supported | ||
""" | ||
bng_parameters = {} | ||
for change in parameter_changes: | ||
if change.parameter.kisao_term \ | ||
and change.parameter.kisao_term.ontology == 'KISAO' \ | ||
and change.parameter.kisao_term.id == '0000211': | ||
# Relative tolerance | ||
bng_parameters["rtol"] = change.value | ||
|
||
elif change.parameter.kisao_term \ | ||
and change.parameter.kisao_term.ontology == 'KISAO' \ | ||
and change.parameter.kisao_term.id == '0000209': | ||
# Absolute tolerance | ||
bng_parameters["atol"] = change.value | ||
|
||
else: | ||
raise NotImplementedError("Parameter {} is not supported".format(change.parameter.id)) | ||
|
||
return bng_parameters | ||
|
||
def convert_simulation_results(self, gdat_filename, out_filename, out_format): | ||
""" Convert simulation results from gdat to the desired output format | ||
Args: | ||
gdat_filename (:obj:`str`): path to simulation results in gdat format | ||
out_filename (:obj:`str`): path to save the results of the simulation | ||
out_format (:obj:`str`): format to save the results of the simulation (e.g., `csv`) | ||
Raises: | ||
:obj:`NotImplementedError`: if the desired output format is not supported | ||
""" | ||
data = self.read_simulation_results(gdat_filename) | ||
if out_format == 'csv': | ||
data.to_csv(out_filename) | ||
elif out_format == 'tsv': | ||
data.to_csv(out_filename, sep='\t') | ||
else: | ||
raise NotImplementedError('Unsupported output format {}'.format(out_format)) | ||
|
||
def read_simulation_results(self, gdat_filename): | ||
""" Read the results of a simulation | ||
Args: | ||
gdat_filename (:obj:`str`): path to simulation results in gdat format | ||
Returns: | ||
:obj:`pandas.DataFrame`: simulation results | ||
""" | ||
with open(gdat_filename, "r") as file: | ||
# Get column names from first line of file | ||
line = file.readline() | ||
names = re.split(r'\s+', (re.sub('#', '', line)).strip()) | ||
|
||
# Read results | ||
results = pandas.read_table(file, sep=r'\s+', header=None, names=names) | ||
|
||
# Set the time as index | ||
results = results.set_index("time") | ||
|
||
# Return results | ||
return results |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,57 @@ | ||
# Author: Ali Sinan Saglam | ||
# Date: 3/30/2020 | ||
# Made for: BioNetGen docker image for BioSim | ||
# BioSimulations-compliant Docker image for BioNetGen <https://bionetgen.org> | ||
# | ||
# Build image: | ||
# docker build \ | ||
# --tag crbm/biosimulations_bionetgen:2.5.0 \ | ||
# --tag crbm/biosimulations_bionetgen:latest \ | ||
# . | ||
# | ||
# Run image: | ||
# docker run \ | ||
# --tty \ | ||
# --rm \ | ||
# --mount type=bind,source="$(pwd)"/tests/fixtures,target=/root/in,readonly \ | ||
# --mount type=bind,source="$(pwd)"/tests/results,target=/root/out \ | ||
# crbm/biosimulations_bionetgen:latest \ | ||
# -i /root/in/test.omex \ | ||
# -o /root/out | ||
# | ||
# Author: Ali Sinan Saglam <als251@pitt.edu> | ||
# Author: Jonathan Karr <karr@mssm.edu> | ||
# Date: 2020-04-13 | ||
|
||
# FROM ubuntu:18.04 | ||
FROM continuumio/anaconda3 | ||
|
||
# update | ||
RUN apt-get update | ||
# stuff we want | ||
RUN apt-get install -y g++ vim perl cmake wget | ||
# Get necessary libraries, SED-ML lib in particular | ||
RUN pip install python-libsedml | ||
# install requirements and BioNetGet | ||
RUN apt-get update -y \ | ||
&& apt-get install --no-install-recommends -y \ | ||
cmake \ | ||
g++ \ | ||
git \ | ||
make \ | ||
perl \ | ||
vim \ | ||
\ | ||
&& git clone https://github.com/RuleWorld/bionetgen /root/bionetgen \ | ||
&& cd /root/bionetgen \ | ||
&& git submodule init \ | ||
&& git submodule update \ | ||
&& cd /root/bionetgen/bng2 \ | ||
&& make \ | ||
\ | ||
&& apt-get remove -y \ | ||
cmake \ | ||
g++ \ | ||
git \ | ||
make \ | ||
&& apt-get autoremove -y \ | ||
&& rm -rf /var/lib/apt/lists/* | ||
ENV PATH=${PATH}:/root/bionetgen/bng2 | ||
|
||
# install bionetgen | ||
WORKDIR /home/BNGDocker/ | ||
RUN git clone https://github.com/RuleWorld/bionetgen | ||
WORKDIR /home/BNGDocker/bionetgen/ | ||
RUN git submodule init | ||
RUN git submodule update | ||
WORKDIR /home/BNGDocker/bionetgen/bng2/ | ||
RUN make | ||
WORKDIR /home/BNGDocker/simulation | ||
# install BioSimulations-compliant command-line interface to BioNetGen | ||
COPY . /root/Biosimulations_BioNetGen | ||
RUN pip3 install /root/Biosimulations_BioNetGen | ||
|
||
# Copy over run script | ||
COPY assets/run-bionetgen.py /usr/local/bin/run-bionetgen | ||
RUN chmod ugo+x /usr/local/bin/run-bionetgen | ||
|
||
# Temporary files for testing | ||
COPY assets/test.bngl /home/BNGDocker/test.bngl | ||
COPY assets/test.xml /home/BNGDocker/test.xml | ||
|
||
# Entry point setup | ||
ENTRYPOINT ["run-bionetgen"] | ||
# if we need to setup some defaults | ||
# CMD [] | ||
# setup entry point | ||
ENTRYPOINT ["bionetgen"] | ||
CMD [] |
Oops, something went wrong.