diff --git a/gillespy/__init__.py b/gillespy/__init__.py index cd3a064..71b2f78 100644 --- a/gillespy/__init__.py +++ b/gillespy/__init__.py @@ -1 +1,4 @@ from .gillespy import * +import os +os.environ["PATH"] += os.pathsep + "/usr/local/stochss/StochKit/" +os.environ["PATH"] += os.pathsep + "/usr/local/stochss/ode/" diff --git a/gillespy/gillespy.py b/gillespy/gillespy.py index 3969d15..18043f6 100644 --- a/gillespy/gillespy.py +++ b/gillespy/gillespy.py @@ -12,9 +12,12 @@ import matplotlib.pyplot as plt import tempfile import uuid -import subprocess +import subprocess32 as subprocess import types import random +import os +import signal +import psutil try: import lxml.etree as etree @@ -239,14 +242,14 @@ def delete_reaction(self, obj): def delete_all_reactions(self): self.listOfReactions.clear() - def run(self, number_of_trajectories=1, seed=None, report_level=0, solver=None, stochkit_home=None, debug=False): + def run(self, number_of_trajectories=1, seed=None, report_level=0, solver=None, stochkit_home=None, debug=False,timeout=None): if solver is not None: if isinstance(solver, (type, types.ClassType)) and issubclass(solver, GillesPySolver): - return solver.run(self,t=self.tspan[-1],increment=self.tspan[-1]-self.tspan[-2],seed=seed,number_of_trajectories=number_of_trajectories, stochkit_home=stochkit_home, debug=debug) + return solver.run(self,t=self.tspan[-1],increment=self.tspan[-1]-self.tspan[-2],seed=seed,number_of_trajectories=number_of_trajectories, stochkit_home=stochkit_home, debug=debug,timeout=timeout) else: raise SimuliationError('argument "solver" to run() must be a subclass of GillesPySolver') else: - return StochKitSolver.run(self,t=self.tspan[-1],increment=self.tspan[-1]-self.tspan[-2],seed=seed,number_of_trajectories=number_of_trajectories, stochkit_home=stochkit_home, debug=debug) + return StochKitSolver.run(self,t=self.tspan[-1],increment=self.tspan[-1]-self.tspan[-2],seed=seed,number_of_trajectories=number_of_trajectories, stochkit_home=stochkit_home, debug=debug,timeout=timeout) class Species(): @@ -318,7 +321,7 @@ class Reaction(): """ def __init__(self, name = "", reactants = {}, products = {}, - propensity_function = None, massaction = False, + propensity_function = None, massaction = True, rate=None, annotation=None): """ Initializes the reaction using short-hand notation. @@ -343,8 +346,8 @@ def __init__(self, name = "", reactants = {}, products = {}, self.name = name self.annotation = "" - if rate is None and propensity_function is None: - raise ReactionError("You must specify either a mass-action rate or a propensity function") + #if rate is None and propensity_function is None: + # raise ReactionError("You must specify either a mass-action rate or a propensity function") # We might use this flag in the future to automatically generate # the propensity function if set to True. @@ -378,10 +381,12 @@ def __init__(self, name = "", reactants = {}, products = {}, if self.massaction: self.type = "mass-action" if rate is None: - raise ReactionError("Reaction : A mass-action propensity has\ - to have a rate.") - self.marate = rate - self.create_mass_action() + self.marate=None + # raise ReactionError("Reaction : A mass-action propensity has\ + # to have a rate.") + else: + self.marate = rate + self.create_mass_action() else: self.type = "customized" @@ -642,6 +647,9 @@ def to_model(self,name): type = reac.find('Type').text except: raise InvalidStochMLError("No reaction type specified.") + + #reaction = Reaction(name,{},{},None,False,0.0,None) + reactants = reac.find('Reactants') try: @@ -820,7 +828,7 @@ class GillesPySolver(): def run(self, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, stochkit_home=None, algorithm=None, - job_id=None, extra_args='', debug=False): + job_id=None, extra_args='', debug=False,timeout=None): """ Call out and run the solver. Collect the results. """ @@ -873,6 +881,7 @@ def run(self, model, t=20, number_of_trajectories=1, algorithm)): executable = os.path.join(os.environ.get('STOCHKIT_HOME'), algorithm) + if executable is None: # try to find the executable in the path if os.environ.get('PATH') is not None: @@ -910,22 +919,37 @@ def run(self, model, t=20, number_of_trajectories=1, print "cmd: {0}".format(cmd) # Execute - try: + #try: #print "CMD: {0}".format(cmd) - handle = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - return_code = handle.wait() - except OSError as e: - raise SimuliationError("Solver execution failed: \ - {0}\n{1}".format(cmd, e)) - + #handle = subprocess.Popen(cmd, stdout = subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + handle = subprocess.Popen(cmd,shell=True, preexec_fn=os.setsid) + #return_code = handle.wait() try: - stderr = handle.stderr.read() - except Exception as e: - stderr = 'Error reading stderr: {0}'.format(e) - try: - stdout = handle.stdout.read() - except Exception as e: - stdout = 'Error reading stdout: {0}'.format(e) + outs, errs = handle.communicate(timeout=timeout) + return_code = 0 + except subprocess.TimeoutExpired: + #os.killpg(os.getpgid(handle.pid),signal.SIGTERM) + #print os.getpgid(handle.pid) + parent = psutil.Process(handle.pid) + for child in parent.children(recursive=True): # or parent.children() for recursive=False + child.kill() + handle.kill() + outs, errs = handle.communicate(timeout=timeout) + return_code=-1 + raise SolverTimeoutError("Solver timed out") + + #except OSError as e: + # raise SimuliationError("Solver execution failed: \ + # {0}\n{1}".format(cmd, e)) + + #try: + # stderr = handle.stderr.read() + #except Exception as e: + # stderr = 'Error reading stderr: {0}'.format(e) + #try: + # stdout = handle.stdout.read() + #except Exception as e: + # stdout = 'Error reading stdout: {0}'.format(e) if return_code != 0: #print stdout @@ -961,7 +985,7 @@ class StochKitSolver(GillesPySolver): @classmethod def run(cls, model, t=20, number_of_trajectories=1, increment=0.05, seed=None, stochkit_home=None, algorithm='ssa', - job_id=None, method=None,debug=False): + job_id=None, method=None,debug=False,timeout=None): # all this is specific to StochKit if model.units == "concentration": @@ -993,7 +1017,7 @@ def run(cls, model, t=20, number_of_trajectories=1, self = StochKitSolver() - return GillesPySolver.run(self, model,t, number_of_trajectories, increment, seed, stochkit_home, algorithm, job_id, extra_args=args, debug=debug) + return GillesPySolver.run(self, model,t, number_of_trajectories, increment, seed, stochkit_home, algorithm, job_id, extra_args=args, debug=debug,timeout=timeout) def get_trajectories(self, outdir, debug=False): # Collect all the output data @@ -1048,6 +1072,9 @@ class InvalidStochMLError(Exception): class InvalidModelError(Exception): pass +class SolverTimeoutError(Exception): + pass + class SimulationError(Exception): pass