diff --git a/spatialpy/__init__.py b/spatialpy/__init__.py index 5599fea0..d62ab566 100644 --- a/spatialpy/__init__.py +++ b/spatialpy/__init__.py @@ -22,13 +22,9 @@ import sys if (sys.version_info < (3,0)): raise Exception("SpatialPy only works in Python 3.0 and higher") +from .__version__ import __version__, __title__, __description__, __url__ +from .__version__ import __author__, __email__ +from .__version__ import __license__, __copyright__ -from spatialpy.Model import * -from spatialpy.Solver import * -from spatialpy.Geometry import * -from spatialpy.Domain import * -from spatialpy.DataFunction import DataFunction -from spatialpy.InitialCondition import * -from spatialpy.BoundaryCondition import BoundaryCondition -from spatialpy.VTKReader import * -from spatialpy.cleanup import * +from spatialpy.core import * +from spatialpy.solvers import * diff --git a/spatialpy/BoundaryCondition.py b/spatialpy/core/BoundaryCondition.py similarity index 90% rename from spatialpy/BoundaryCondition.py rename to spatialpy/core/BoundaryCondition.py index eaa06bf5..0077c3fc 100644 --- a/spatialpy/BoundaryCondition.py +++ b/spatialpy/core/BoundaryCondition.py @@ -17,7 +17,8 @@ ''' import numpy -from spatialpy.Model import ModelError + +from spatialpy.core.spatialpyError import BoundaryConditionError class BoundaryCondition(): @@ -102,9 +103,9 @@ def expression(self): :rtype: str """ if( self.species is not None and self.property is not None): - raise ModelError("Can not set both species and property") + raise BoundaryConditionError("Can not set both species and property") if self.value is None: - raise ModelError("Must set value") + raise BoundaryConditionError("Must set value") cond=[] if(self.xmin is not None): cond.append("(me->x[0] >= {0})".format(self.xmin)) if(self.xmax is not None): cond.append("(me->x[0] <= {0})".format(self.xmax)) @@ -113,14 +114,14 @@ def expression(self): if(self.zmin is not None): cond.append("(me->x[2] >= {0})".format(self.zmin)) if(self.zmax is not None): cond.append("(me->x[2] <= {0})".format(self.zmax)) if(self.type_id is not None): cond.append("(me->type == {0})".format(int(self.type_id))) - if(len(cond)==0): raise ModelError('need at least one condition on the BoundaryCondition') + if(len(cond)==0): raise BoundaryConditionError('need at least one condition on the BoundaryCondition') bcstr = "if(" + '&&'.join(cond) + "){" if self.species is not None: if self.deterministic: s_ndx = self.model.species_map[self.model.listOfSpecies[self.species]] bcstr += "me->C[{0}] = {1};".format(s_ndx,self.value) else: - raise Exception("BoundaryConditions don't work for stochastic species yet") + raise BoundaryConditionError("BoundaryConditions don't work for stochastic species yet") elif self.property is not None: if(self.property == 'v'): for i in range(3): @@ -130,6 +131,6 @@ def expression(self): elif(self.property == 'rho'): bcstr+= "me->rho={0};".format(self.value) else: - raise Exception("Unable handle boundary condition for property '{0}'".format(self.property)) + raise BoundaryConditionError("Unable handle boundary condition for property '{0}'".format(self.property)) bcstr+= "}" return bcstr diff --git a/spatialpy/DataFunction.py b/spatialpy/core/DataFunction.py similarity index 86% rename from spatialpy/DataFunction.py rename to spatialpy/core/DataFunction.py index 9f1bd51b..e6b12d39 100644 --- a/spatialpy/DataFunction.py +++ b/spatialpy/core/DataFunction.py @@ -15,7 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' - +from spatialpy.core.spatialpyError import DataFunctionError class DataFunction(): """ Abstract class used to constuct the data function. """ @@ -24,7 +24,7 @@ def __init__(self, name=None): if name is not None: self.name = name if self.name is None: - raise Exception("DataFunction must have a 'name'") + raise DataFunctionError("DataFunction must have a 'name'") def map(self, x): """ @@ -39,6 +39,6 @@ def map(self, x): :returns: value of function at this spatial location. :rtype: float """ - raise Exception("DataFunction.map() must be implemented.") + raise DataFunctionError("DataFunction.map() must be implemented.") diff --git a/spatialpy/Domain.py b/spatialpy/core/Domain.py similarity index 99% rename from spatialpy/Domain.py rename to spatialpy/core/Domain.py index c97a3727..40155ac2 100644 --- a/spatialpy/Domain.py +++ b/spatialpy/core/Domain.py @@ -22,8 +22,7 @@ import numpy from scipy.spatial import KDTree -from spatialpy.Result import _plotly_iterate - +from spatialpy.core.spatialpyError import DomainError class Domain(): """ Domain class for SpatialPy. A domain defines points and attributes of a regional space for simulation. @@ -360,6 +359,7 @@ def plot_types(self, width=None, height=None, colormap=None, size=5, title=None, :param use_matplotlib: Whether or not to plot the proprties results using matplotlib. :type use_matplotlib: bool ''' + from spatialpy.core.Result import _plotly_iterate if use_matplotlib: width = 6.4 if width in (None, "auto") else width @@ -557,7 +557,7 @@ def read_stochss_subdomain_file(self, filename): (ndx,type_id) = line.rstrip().split(',') self.type[int(ndx)] = int(type_id) except ValueError as e: - raise ModelError(f"Could not read in subdomain file, error on line {ln}: {line}") + raise DomainError(f"Could not read in subdomain file, error on line {ln}: {line}") @classmethod @@ -751,8 +751,3 @@ def create_2D_domain(cls, xlim, ylim, nx, ny, type_id=1, mass=1.0, nu=1.0, rho=N # return model ref return obj - - - -class DomainError(Exception): - pass diff --git a/spatialpy/Geometry.py b/spatialpy/core/Geometry.py similarity index 90% rename from spatialpy/Geometry.py rename to spatialpy/core/Geometry.py index ca0b291a..3e086bc3 100644 --- a/spatialpy/Geometry.py +++ b/spatialpy/core/Geometry.py @@ -15,6 +15,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . ''' +from spatialpy.core.spatialpyError import GeometryError class Geometry: """ Geometry class provides a method for tagging parts of the spatial domain as separate parts""" @@ -23,7 +24,7 @@ def __init__(self): pass def inside(self, x, on_boundary): - raise Exception("Subclasses of spatialpy.Geometry must implement the inside() method") + raise GeometryError("Subclasses of spatialpy.Geometry must implement the inside() method") class GeometryAll(Geometry): diff --git a/spatialpy/InitialCondition.py b/spatialpy/core/InitialCondition.py similarity index 91% rename from spatialpy/InitialCondition.py rename to spatialpy/core/InitialCondition.py index c682050a..5469e171 100644 --- a/spatialpy/InitialCondition.py +++ b/spatialpy/core/InitialCondition.py @@ -17,7 +17,8 @@ ''' import numpy -from spatialpy.Model import ModelError + +from spatialpy.core.spatialpyError import InitialConditionError class InitialCondition(): @@ -27,7 +28,7 @@ class InitialCondition(): """ def apply(self, model): - raise ModelError("spatialpy.InitialCondition subclasses must implement apply()") + raise InitialConditionError("spatialpy.InitialCondition subclasses must implement apply()") #TODO: implement InitialConditionFromResult() @@ -102,7 +103,7 @@ def apply(self, model): if model.domain.type[i] in self.types: allowed_voxels.append(i) nvox = len(allowed_voxels) - if nvox==0: raise ModelError("ScatterInitialCondition has zero voxels to scatter in. Species={0} count={1} types={2}".format(self.species.name, self.count, self.types)) + if nvox==0: raise InitialConditionError("ScatterInitialCondition has zero voxels to scatter in. Species={0} count={1} types={2}".format(self.species.name, self.count, self.types)) for mol in range(self.count): v_ndx = numpy.random.randint(0, nvox) vtx = allowed_voxels[v_ndx] diff --git a/spatialpy/Model.py b/spatialpy/core/Model.py similarity index 70% rename from spatialpy/Model.py rename to spatialpy/core/Model.py index 57e33262..84919e04 100644 --- a/spatialpy/Model.py +++ b/spatialpy/core/Model.py @@ -18,16 +18,22 @@ #This module defines a model that simulates a discrete, stoachastic, mixed biochemical reaction network in python. -import uuid -from collections import OrderedDict -from spatialpy.Solver import Solver -from spatialpy.DataFunction import DataFunction -from spatialpy.Domain import Domain -from spatialpy.expression import Expression import numpy import scipy import warnings import math +import uuid +from collections import OrderedDict + +from spatialpy.core.Species import Species +from spatialpy.core.Parameter import Parameter +from spatialpy.core.Reaction import Reaction +from spatialpy.core.DataFunction import DataFunction +from spatialpy.core.Domain import Domain +from spatialpy.solvers.Solver import Solver +from spatialpy.solvers.build.expression import Expression + +from spatialpy.core.spatialpyError import * def export_StochSS(spatialpy_model, filename=None, return_stochss_model=False): @@ -726,294 +732,3 @@ def _apply_initial_conditions(self): # apply initial condition functions for ic in self.listOfInitialConditions: ic.apply(self) - - - - - -class Species(): - """ Model of a biochemical species. Must be assigned a diffusion coefficent. - - :param name: Name of the Species - :type name: str - :param diffusion_coefficient: non-constant coefficient of diffusion for Species - :type diffusion_coefficient: float - """ - - reserved_names = ["x", "vol","sd","data_fn","t","debug_flag","Spatialpy"] - - - - def __init__(self,name=None, diffusion_coefficient=None): - # A species has a name (string) and an initial value (positive integer) - if name is None: - raise ModelError("Species must have a name") - else: - self.name = name - if diffusion_coefficient is not None: - self.diffusion_coefficient=diffusion_coefficient - else: - raise ModelError("Species must have a diffusion_coefficient") - - - def __str__(self): - print_string = f"{self.name}: {str(self.diffusion_coefficient)}" - return print_string - -class Parameter(): - """ - Model of a rate paramter. - A parameter can be given as a String expression (function) or directly as a scalar value. - If given a String expression, it should be evaluable in the namespace of a parent Model. - - :param name: Name of the Parameter - :type name: str - :param expression: Mathematical expression of Parameter - :type expression: str - :param value: Parameter as value rather than expression. - :type value: float - - """ - - def __init__(self,name=None,expression=None): - - if name is None: - raise ParameterError("name is required for a Parameter.") - if expression is None: - raise ParameterError("expression is required for a Parameter.") - - self.name = name - self.value = None - # We allow expression to be passed in as a non-string type. Invalid strings - # will be caught below. It is perfectly fine to give a scalar value as the expression. - # This can then be evaluated in an empty namespace to the scalar value. - self.expression = str(expression) - - def __str__(self): - print_string = f"{self.name}: {str(self.expression)}" - return print_string - - def _evaluate(self,namespace={}): - """ Evaluate the expression and return the (scalar) value """ - try: - self.value = (float(eval(self.expression, namespace))) - except Exception as err: - message = f"Could not evaluate expression '{self.expression}': {err}." - raise ParameterError(message) from err - - -class Reaction(): - """ - Models a biochemical reaction. A reaction conatains dictionaries of species (reactants and products) \ - and parameters. The reaction's propensity function needs to be evaluable and result in a \ - non-negative scalar value in the namespace defined by the union of its Reactant, Product and \ - Parameter dictionaries. If massaction is set to true, propensity_function is not a valid argument. \ - Instead, the propensity function is constructed automatically. For mass-action, zeroth, first \ - and second order reactions are supported, attempting to used higher orders will result in an error. - - :param name: String that the model is referenced by - :type name: str - :param parameters: A list of parameter instances - :type parameters: list(spatialpy.Model.Parameter) - :param propensity_function: String with the expression for the reaction's propensity - :type propensity_function: str - :param reactants: Dictionary of {species:stoichiometry} of reaction reactants - :type reactants: dict - :param products: Dictionary of {species:stoichiometry} of reaction products - :type products: dict - :param annotation: Description of the reaction (meta) - :type annotation: str - :param massaction: Is the reaction of mass action type or not? - :type massaction: bool - :param rate: if mass action, rate is a reference to a parameter instance. - :type rate: spatialpy.model.Parameter - - - """ - - def __init__(self, name = "", reactants = {}, products = {}, propensity_function=None, massaction=None, rate=None, annotation=None,restrict_to=None): - - # Metadata - self.name = name - self.reactants = reactants - self.products = products - self.propensity_function = propensity_function - self.massaction = massaction - self.rate = rate - self.annotation = annotation - if isinstance(restrict_to, int): - self.restrict_to = [restrict_to] - elif isinstance(restrict_to, list) or restrict_to is None: - self.restrict_to = restrict_to - else: - errmsg = f"Reaction {name}: restrict_to must be an integer or list of integers." - raise ReactionError(errmsg) - - def initialize(self, model): - """ Defered object initialization, called by model.add_reaction(). """ - - self.ode_propensity_function = self.propensity_function - - if self.propensity_function is None: - if self.rate is None: - errmsg = f"Reaction {name}: You must either set the reaction to be mass-action or specifiy a propensity function." - raise ReactionError(errmsg) - self.massaction = True - else: - if self.rate is not None: - errmsg = f"Reaction {name}: You cannot set the propensity type to mass-action and simultaneously set a propensity function." - raise ReactionError(errmsg) - # If they don't give us a propensity function and do give a rate, assume mass-action. - self.massaction = False - self.marate = None - - - reactants = self.reactants - self.reactants = {} - if reactants is not None: - for r in reactants: - rtype = type(r).__name__ - if rtype=='Species': - self.reactants[r]=reactants[r] - elif rtype=='str': - if r not in model.listOfSpecies: - raise ReactionError(f"Could not find species '{r}' in model.") - self.reactants[model.listOfSpecies[r]] = reactants[r] - - products = self.products - self.products = {} - if products is not None: - for p in products: - rtype = type(p).__name__ - if rtype=='Species': - self.products[p]=products[p] - else: - if p not in model.listOfSpecies: - raise ReactionError(f"Could not find species '{p}' in model.") - self.products[model.listOfSpecies[p]] = products[p] - - if self.massaction: - self.type = "mass-action" - rtype = type(self.rate).__name__ - if rtype == 'Parameter': - self.marate = self.rate.name - elif rtype == 'int' or rtype == 'float': - self.marate = str(self.rate) - else: - self.marate = self.rate - self.__create_mass_action() - else: - self.type = "customized" - - - def __str__(self): - print_string = f"{self.name}, Active in: {str(self.restrict_to)}" - if len(self.reactants): - print_string += f"\n\tReactants" - for species, stoichiometry in self.reactants.items(): - name = species if isinstance(species, str) else species.name - print_string += f"\n\t\t{name}: {stoichiometry}" - if len(self.products): - print_string += f"\n\tProducts" - for species, stoichiometry in self.products.items(): - name = species if isinstance(species, str) else species.name - print_string += f"\n\t\t{name}: {stoichiometry}" - print_string += f"\n\tPropensity Function: {self.propensity_function}" - return print_string - - - def __create_mass_action(self): - """ Create a mass action propensity function given self.reactants and a single parameter value. - - We support zeroth, first and second order propensities only. - There is no theoretical justification for higher order propensities. - Users can still create such propensities if they really want to, - but should then use a custom propensity. - """ - total_stoch = 0 - for r in self.reactants: - total_stoch += self.reactants[r] - if total_stoch > 2: - raise ReactionError( - """Reaction: A mass-action reaction cannot involve more than two of one species or one " - of two species (no more than 2 total reactants). - SpatialPy support zeroth, first and second order propensities only. - There is no theoretical justification for higher order propensities. - Users can still create such propensities using a 'custom propensity'.""") - # Case EmptySet -> Y - - if isinstance(self.marate, str): - propensity_function = self.marate - ode_propensity_function = self.marate - else: - propensity_function = self.marate.name - ode_propensity_function = self.marate.name - - # There are only three ways to get 'total_stoch==2': - for r in self.reactants: - # Case 1: 2X -> Y - if self.reactants[r] == 2: - propensity_function = ("0.5*" + propensity_function + - "*" + r.name + "*(" + r.name + "-1)/vol") - else: - # Case 3: X1, X2 -> Y; - propensity_function += "*" + r.name - ode_propensity_function += "*" + r.name - - # Set the volume dependency based on order. - order = len(self.reactants) - if order == 2: - propensity_function += "/vol" - elif order == 0: - propensity_function += "*vol" - - self.propensity_function = propensity_function - self.ode_propensity_function = ode_propensity_function - - - def add_reactant(self,S,stoichiometry): - """ Add a reactant to this reaction - - :param s: reactant Species object - :type s: spatialpy.Model.Species - :param stoichiometry: Stoichiometry of this participant reactant - :type stoichiometry: int - - """ - if stoichiometry <= 0: - raise ReactionError("Reaction "+self.name+"Stoichiometry must be a positive integer.") - self.reactants[S.name]=stoichiometry - - def add_product(self,S,stoichiometry): - """ Add a product to this reaction - - :param s: Species object to be produced by the reaction - :type s: spatialpy.Model.Species - :param stoichiometry: Stoichiometry of this product. - :type stoichiometry: int - - """ - self.products[S.name]=stoichiometry - - def annotate(self,annotation): - """ Add an annotation to this reaction. - - :param annotation: Annotation note to be added to reaction - :type annotation: str - - """ - self.annotation = annotation - - -# Module exceptions -class ModelError(Exception): - pass - -class SpeciesError(ModelError): - pass - -class ReactionError(ModelError): - pass - -class ParameterError(ModelError): - pass diff --git a/spatialpy/core/Parameter.py b/spatialpy/core/Parameter.py new file mode 100644 index 00000000..40099c0b --- /dev/null +++ b/spatialpy/core/Parameter.py @@ -0,0 +1,64 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' +from spatialpy.core.spatialpyError import ParameterError + +class Parameter(): + """ + Model of a rate paramter. + A parameter can be given as a String expression (function) or directly as a scalar value. + If given a String expression, it should be evaluable in the namespace of a parent Model. + + :param name: Name of the Parameter + :type name: str + :param expression: Mathematical expression of Parameter + :type expression: str + :param value: Parameter as value rather than expression. + :type value: float + """ + + def __init__(self, name=None, expression=None): + + if name is None: + raise ParameterError("name is required for a Parameter.") + if not isinstance(name, str): + raise ParameterError("Parameter name must be a string.") + + if expression is None: + raise ParameterError("expression is required for a Parameter.") + + self.name = name + self.value = None + # We allow expression to be passed in as a non-string type. Invalid strings + # will be caught below. It is perfectly fine to give a scalar value as the expression. + # This can then be evaluated in an empty namespace to the scalar value. + if isinstance(expression, (int, float)): + self.expression = str(expression) + else: + self.expression = expression + + def __str__(self): + print_string = f"{self.name}: {str(self.expression)}" + return print_string + + def _evaluate(self, namespace={}): + """ Evaluate the expression and return the (scalar) value """ + try: + self.value = (float(eval(self.expression, namespace))) + except Exception as err: + message = f"Could not evaluate expression '{self.expression}': {err}." + raise ParameterError(message) from err diff --git a/spatialpy/core/Reaction.py b/spatialpy/core/Reaction.py new file mode 100644 index 00000000..3b31c9f8 --- /dev/null +++ b/spatialpy/core/Reaction.py @@ -0,0 +1,238 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' +from spatialpy.core.Parameter import Parameter +from spatialpy.core.spatialpyError import ReactionError + +class Reaction(): + """ + Models a biochemical reaction. A reaction conatains dictionaries of species (reactants and products) \ + and parameters. The reaction's propensity function needs to be evaluable and result in a \ + non-negative scalar value in the namespace defined by the union of its Reactant, Product and \ + Parameter dictionaries. For mass-action, zeroth, first \ + and second order reactions are supported, attempting to used higher orders will result in an error. + + :param name: String that the model is referenced by + :type name: str + :param parameters: A list of parameter instances + :type parameters: list(spatialpy.Model.Parameter) + :param propensity_function: String with the expression for the reaction's propensity + :type propensity_function: str + :param reactants: Dictionary of {species:stoichiometry} of reaction reactants + :type reactants: dict + :param products: Dictionary of {species:stoichiometry} of reaction products + :type products: dict + :param annotation: Description of the reaction (meta) + :type annotation: str + :param rate: if mass action, rate is a reference to a parameter instance. + :type rate: spatialpy.model.Parameter + """ + + def __init__(self, name="", reactants={}, products={}, propensity_function=None, rate=None, annotation=None, restrict_to=None): + + if not isinstance(name, str): + raise ReactionError("Reaction name must be of type str.") + self.name = name + + if not isinstance(reactants, dict): + raise ReactionError("Reaction reactants must be of type dict.") + self.reactants = reactants + + if not isinstance(products, dict): + raise ReactionError("Reaction products must be of type dict.") + self.products = products + + if not (isinstance(propensity_function, (str, int, float)) or propensity_function is None): + raise ReactionError("Reaction propensity_function must be one of the following types: str, int, float, or None.") + if isinstance(propensity_function, (int, float)): + self.propensity_function = str(propensity_function) + else: + self.propensity_function = propensity_function + + if not (isinstance(rate, (str, int, float)) or rate is None or \ + isinstance(rate, Parameter) or type(rate).__name__ == 'Parameter'): + raise ReactionError("Reaction rate must be one of the following types: spatialpy.Parameter, str, int, float, or None.") + if isinstance(rate, (int, float)): + self.rate = str(rate) + else: + self.rate = rate + + if isinstance(restrict_to, int): + self.restrict_to = [restrict_to] + elif isinstance(restrict_to, list) or restrict_to is None: + self.restrict_to = restrict_to + else: + errmsg = f"Reaction {name}: restrict_to must be an integer or list of integers." + raise ReactionError(errmsg) + + self.annotation = annotation + + + def initialize(self, model): + """ Defered object initialization, called by model.add_reaction(). """ + + self.ode_propensity_function = self.propensity_function + + if self.propensity_function is None: + if self.rate is None: + errmsg = f"Reaction {name}: You must either set the reaction to be mass-action or specifiy a propensity function." + raise ReactionError(errmsg) + self.massaction = True + else: + if self.rate is not None: + errmsg = f"Reaction {name}: You cannot set the propensity type to mass-action and simultaneously set a propensity function." + raise ReactionError(errmsg) + # If they don't give us a propensity function and do give a rate, assume mass-action. + self.massaction = False + self.marate = None + + + reactants = self.reactants + self.reactants = {} + if reactants is not None: + for r in reactants: + rtype = type(r).__name__ + if rtype=='Species': + self.reactants[r]=reactants[r] + elif rtype=='str': + if r not in model.listOfSpecies: + raise ReactionError(f"Could not find species '{r}' in model.") + self.reactants[model.listOfSpecies[r]] = reactants[r] + + products = self.products + self.products = {} + if products is not None: + for p in products: + rtype = type(p).__name__ + if rtype=='Species': + self.products[p]=products[p] + else: + if p not in model.listOfSpecies: + raise ReactionError(f"Could not find species '{p}' in model.") + self.products[model.listOfSpecies[p]] = products[p] + + if self.massaction: + self.type = "mass-action" + rtype = type(self.rate).__name__ + if rtype == 'Parameter': + self.marate = self.rate.name + elif rtype == 'int' or rtype == 'float': + self.marate = str(self.rate) + else: + self.marate = self.rate + self.__create_mass_action() + else: + self.type = "customized" + + + def __str__(self): + print_string = f"{self.name}, Active in: {str(self.restrict_to)}" + if len(self.reactants): + print_string += f"\n\tReactants" + for species, stoichiometry in self.reactants.items(): + name = species if isinstance(species, str) else species.name + print_string += f"\n\t\t{name}: {stoichiometry}" + if len(self.products): + print_string += f"\n\tProducts" + for species, stoichiometry in self.products.items(): + name = species if isinstance(species, str) else species.name + print_string += f"\n\t\t{name}: {stoichiometry}" + print_string += f"\n\tPropensity Function: {self.propensity_function}" + return print_string + + + def __create_mass_action(self): + """ Create a mass action propensity function given self.reactants and a single parameter value. + + We support zeroth, first and second order propensities only. + There is no theoretical justification for higher order propensities. + Users can still create such propensities if they really want to, + but should then use a custom propensity. + """ + total_stoch = 0 + for r in self.reactants: + total_stoch += self.reactants[r] + if total_stoch > 2: + raise ReactionError( + """Reaction: A mass-action reaction cannot involve more than two of one species or one " + of two species (no more than 2 total reactants). + SpatialPy support zeroth, first and second order propensities only. + There is no theoretical justification for higher order propensities. + Users can still create such propensities using a 'custom propensity'.""") + # Case EmptySet -> Y + + if isinstance(self.marate, str): + propensity_function = self.marate + ode_propensity_function = self.marate + else: + propensity_function = self.marate.name + ode_propensity_function = self.marate.name + + # There are only three ways to get 'total_stoch==2': + for r in self.reactants: + # Case 1: 2X -> Y + if self.reactants[r] == 2: + propensity_function = ("0.5*" + propensity_function + + "*" + r.name + "*(" + r.name + "-1)/vol") + else: + # Case 3: X1, X2 -> Y; + propensity_function += "*" + r.name + ode_propensity_function += "*" + r.name + + # Set the volume dependency based on order. + order = len(self.reactants) + if order == 2: + propensity_function += "/vol" + elif order == 0: + propensity_function += "*vol" + + self.propensity_function = propensity_function + self.ode_propensity_function = ode_propensity_function + + + def add_reactant(self,S,stoichiometry): + """ Add a reactant to this reaction + + :param s: reactant Species object + :type s: spatialpy.Model.Species + :param stoichiometry: Stoichiometry of this participant reactant + :type stoichiometry: int + + """ + if stoichiometry <= 0: + raise ReactionError("Reaction "+self.name+"Stoichiometry must be a positive integer.") + self.reactants[S.name]=stoichiometry + + def add_product(self,S,stoichiometry): + """ Add a product to this reaction + + :param s: Species object to be produced by the reaction + :type s: spatialpy.Model.Species + :param stoichiometry: Stoichiometry of this product. + :type stoichiometry: int + + """ + self.products[S.name]=stoichiometry + + def annotate(self,annotation): + """ Add an annotation to this reaction. + + :param annotation: Annotation note to be added to reaction + :type annotation: str + + """ + self.annotation = annotation diff --git a/spatialpy/Result.py b/spatialpy/core/Result.py similarity index 99% rename from spatialpy/Result.py rename to spatialpy/core/Result.py index 817c2343..5885221c 100644 --- a/spatialpy/Result.py +++ b/spatialpy/core/Result.py @@ -26,8 +26,9 @@ import numpy -from spatialpy.Model import * -from spatialpy.VTKReader import VTKReader +from spatialpy.core.Model import * +from spatialpy.core.VTKReader import VTKReader +from spatialpy.core.spatialpyError import ResultError try: import vtk @@ -826,7 +827,4 @@ def export_to_vtk(self, timespan, folder_name=None): The exported data is #molecules/volume, where the volume unit is implicit from the mesh dimension. Not currently implemented.""" # TODO - raise Exception("Not implemented.") - -class ResultError(Exception): - pass + raise ResultError("Not implemented.") diff --git a/spatialpy/core/Species.py b/spatialpy/core/Species.py new file mode 100644 index 00000000..ed07e2dc --- /dev/null +++ b/spatialpy/core/Species.py @@ -0,0 +1,67 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' +from spatialpy.core.spatialpyError import SpeciesError + +class Species(): + """ + Model of a biochemical species. Must be assigned a diffusion coefficent. + + :param name: Name of the Species + :type name: str + :param diffusion_coefficient: non-constant coefficient of diffusion for Species + :type diffusion_coefficient: float + """ + + def __init__(self, name=None, diffusion_coefficient=None): + # A species has a name (string) and an initial value (positive integer) + if name is None: + raise SpeciesError("Species must have a name") + if not isinstance(name, str): + raise SpeciesError("Species name must be a string") + + if diffusion_coefficient is None: + raise SpeciesError("Species must have a diffusion_coefficient") + if not isinstance(diffusion_coefficient, (float, int)): + raise SpeciesError("Diffusion coefficient must be a float or int.") + if diffusion_coefficient < 0: + raise SpeciesError("Diffusion coefficient must be non-negative.") + + self.name = name + self.diffusion_coefficient = diffusion_coefficient + + + def __str__(self): + print_string = f"{self.name}: {str(self.diffusion_coefficient)}" + return print_string + + + def set_diffusion_coefficient(self, diffusion_coefficient): + """ + Setter method for non-constant coefficient of diffusion for Species + + :param diffusion_coefficient: Integer to set initial species population + :type diffusion_coefficient: float + + :raises SpeciesError: If diffusion_coefficient is negative or a decimal number + """ + if not isinstance(diffusion_coefficient, (float, int)): + raise SpeciesError("Diffusion coefficient must be a float or int.") + if diffusion_coefficient < 0: + raise SpeciesError("Diffusion coefficient must be non-negative.") + + self.diffusion_coefficient = diffusion_coefficient diff --git a/spatialpy/VTKReader.py b/spatialpy/core/VTKReader.py similarity index 96% rename from spatialpy/VTKReader.py rename to spatialpy/core/VTKReader.py index 45932770..c24bad1d 100644 --- a/spatialpy/VTKReader.py +++ b/spatialpy/core/VTKReader.py @@ -19,6 +19,8 @@ import numpy import math +from spatialpy.core.spatialpyError import VTKReaderIOError + class VTKReader: """VTKReader.py: SpatialPy minimal VTK legacy file reader.""" @@ -218,16 +220,3 @@ def readfile(self): self.arrays = self.readarrays(fd) #if self.debug: print("self.arrays.keys = {0}".format(self.arrays.keys())) - - -class VTKReaderError(Exception): - """Base class for exceptions in VTKReader module.""" - - pass - - -class VTKReaderIOError(VTKReaderError): - """Exception raised for I/O errors.""" - - def __init__(self, message): - self.message = message diff --git a/spatialpy/core/__init__.py b/spatialpy/core/__init__.py new file mode 100644 index 00000000..e12ccfd5 --- /dev/null +++ b/spatialpy/core/__init__.py @@ -0,0 +1,43 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' + +import logging +from .BoundaryCondition import * +from .cleanup import * +from .DataFunction import * +from .Domain import * +from .Geometry import * +from .InitialCondition import * +from .Model import * +from .Parameter import * +from .Reaction import * +from .Result import * +from .spatialpyError import * +from .Species import * +from .VTKReader import * +from spatialpy.__version__ import __version__ + +_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +_handler = logging.StreamHandler() +_handler.setFormatter(_formatter) +version = __version__ +log = logging.getLogger() +log.setLevel(logging.WARNING) +log.addHandler(_handler) + +__all__ = [s for s in dir() if not s.startswith('_')] diff --git a/spatialpy/cleanup.py b/spatialpy/core/cleanup.py similarity index 85% rename from spatialpy/cleanup.py rename to spatialpy/core/cleanup.py index 66d664d8..1b966b9d 100644 --- a/spatialpy/cleanup.py +++ b/spatialpy/core/cleanup.py @@ -24,6 +24,8 @@ def cleanup_tempfiles(): ''' Cleanup all tempfiles in spatialpy core, build, and results. ''' + from spatialpy.core import log + cleanup_core_files() tempdir = tempfile.gettempdir() for file_obj in os.listdir(tempdir): @@ -36,11 +38,13 @@ def cleanup_core_files(): ''' Cleanup all tempfiles in spatialpy core. ''' + from spatialpy.core import log + tempdir = tempfile.gettempdir() core_dir = os.path.join(tempdir, "spatialpy_core") if os.path.isdir(core_dir): shutil.rmtree(core_dir) - print(f"Spatialpy core directory was removed") + log.info(f"Spatialpy core directory was removed") def cleanup_build_files(build_dir=None): ''' @@ -49,10 +53,11 @@ def cleanup_build_files(build_dir=None): :param build_dir: Path to the build directory to be removed. (optional) :type build_dir: string ''' + from spatialpy.core import log if build_dir is not None: shutil.rmtree(build_dir) - print(f"Build directory'{build_dir}' was removed") + log.info(f"Build directory'{build_dir}' was removed") else: count = 0 tempdir = tempfile.gettempdir() @@ -61,7 +66,7 @@ def cleanup_build_files(build_dir=None): build_dir = os.path.join(tempdir, file_obj) shutil.rmtree(build_dir) count += 1 - print(f"{count} build directories were removed") + log.info(f"{count} build directories were removed") def cleanup_result_files(result_dir=None): ''' @@ -70,9 +75,11 @@ def cleanup_result_files(result_dir=None): :param result_dir: Path to the result directory to be removed. (optional) :type result_dir: string ''' + from spatialpy.core import log + if result_dir is not None: shutil.rmtree(result_dir) - print(f"Result directory '{result_dir}' was removed") + log.info(f"Result directory '{result_dir}' was removed") else: count = 0 tempdir = tempfile.gettempdir() @@ -81,4 +88,4 @@ def cleanup_result_files(result_dir=None): result_dir = os.path.join(tempdir, file_obj) shutil.rmtree(result_dir) count += 1 - print(f"{count} result directories were removed") + log.info(f"{count} result directories were removed") diff --git a/spatialpy/core/spatialpyError.py b/spatialpy/core/spatialpyError.py new file mode 100644 index 00000000..2139d212 --- /dev/null +++ b/spatialpy/core/spatialpyError.py @@ -0,0 +1,82 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' + +# Base Module Expections +class ModelError(Exception): + pass + + +class ResultError(Exception): + pass + + +class VTKReaderError(Exception): + """Base class for exceptions in VTKReader module.""" + pass + + +class SimulationError(Exception): + pass + + +# Model Component Exceptions +class BoundaryConditionError(ModelError): + pass + + +class DataFunctionError(ModelError): + pass + + +class DomainError(ModelError): + pass + + +class GeometryError(ModelError): + pass + + +class InitialConditionError(ModelError): + pass + + +class ParameterError(ModelError): + pass + + +class ReactionError(ModelError): + pass + + +class SpeciesError(ModelError): + pass + + +# Result Exceptions + + +# VTKReader Exceptions +class VTKReaderIOError(VTKReaderError): + """Exception raised for I/O errors.""" + def __init__(self, message): + self.message = message + + +# Simulation Exceptions +class SimulationTimeout(SimulationError): + pass diff --git a/spatialpy/Solver.py b/spatialpy/solvers/Solver.py similarity index 98% rename from spatialpy/Solver.py rename to spatialpy/solvers/Solver.py index e879f1f0..59612e8f 100644 --- a/spatialpy/Solver.py +++ b/spatialpy/solvers/Solver.py @@ -29,8 +29,9 @@ import re -from spatialpy.Model import * -from spatialpy.Result import * +from spatialpy.core.Model import * +from spatialpy.core.Result import * +from spatialpy.core.spatialpyError import * def _read_from_stdout(stdout,verbose=True): ''' Used with subprocess.Popen and threading to capture all output and print @@ -76,7 +77,7 @@ def __init__(self, model, debug_level=0): self.h = None # basis function width self.SpatialPy_ROOT = os.path.dirname( - os.path.abspath(__file__))+"/ssa_sdpd-c-simulation-engine" + os.path.abspath(__file__))+"/c_base/ssa_sdpd-c-simulation-engine" self.SpatialPy_ROOTDIR = self.SpatialPy_ROOT.replace(" ","\\ "); self.SpatialPy_ROOTINC = self.SpatialPy_ROOT.replace(" ","\\\\ "); self.SpatialPy_ROOTPARAM = self.SpatialPy_ROOT.replace(" ","?"); @@ -314,7 +315,7 @@ def __create_propensity_file(self, file_name=None): template = open(os.path.abspath(os.path.dirname( - __file__)) + '/ssa_sdpd-c-simulation-engine/propensity_file_template.cpp', 'r') + __file__)) + '/c_base/ssa_sdpd-c-simulation-engine/propensity_file_template.cpp', 'r') propfile = open(file_name, "w") propfilestr = template.read() @@ -641,11 +642,3 @@ def __create_propensity_file(self, file_name=None): #### Write the data to the file #### propfile.write(propfilestr) propfile.close() - - -class SimulationError(Exception): - pass - - -class SimulationTimeout(SimulationError): - pass diff --git a/spatialpy/solvers/__init__.py b/spatialpy/solvers/__init__.py new file mode 100644 index 00000000..030a087b --- /dev/null +++ b/spatialpy/solvers/__init__.py @@ -0,0 +1,19 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' + +from .Solver import * diff --git a/spatialpy/solvers/build/__init__.py b/spatialpy/solvers/build/__init__.py new file mode 100644 index 00000000..f0969dae --- /dev/null +++ b/spatialpy/solvers/build/__init__.py @@ -0,0 +1,17 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' diff --git a/spatialpy/expression.py b/spatialpy/solvers/build/expression.py similarity index 95% rename from spatialpy/expression.py rename to spatialpy/solvers/build/expression.py index a12dda28..4b464273 100644 --- a/spatialpy/expression.py +++ b/spatialpy/solvers/build/expression.py @@ -1,3 +1,21 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' + import ast from typing import Union, Optional diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/build/Makefile b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/build/Makefile similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/build/Makefile rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/build/Makefile diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/Copyright.txt b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/Copyright.txt similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/Copyright.txt rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/Copyright.txt diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/License.txt b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/License.txt similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/License.txt rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/License.txt diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/Ann.sln b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/Ann.sln similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/Ann.sln rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/Ann.sln diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/Makefile b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/Makefile similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/Makefile rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/Makefile diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/ann2fig/ann2fig.vcproj b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/ann2fig/ann2fig.vcproj similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/ann2fig/ann2fig.vcproj rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/ann2fig/ann2fig.vcproj diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/dll/dll.vcproj b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/dll/dll.vcproj similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/dll/dll.vcproj rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/dll/dll.vcproj diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/sample/sample.vcproj b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/sample/sample.vcproj similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/sample/sample.vcproj rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/sample/sample.vcproj diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/test/test.vcproj b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/test/test.vcproj similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/test/test.vcproj rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/MS_Win32/test/test.vcproj diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/Make-config b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/Make-config similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/Make-config rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/Make-config diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/Makefile b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/Makefile similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/Makefile rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/Makefile diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/ReadMe.txt b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/ReadMe.txt similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/ReadMe.txt rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/ReadMe.txt diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/ann2fig/Makefile b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/ann2fig/Makefile similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/ann2fig/Makefile rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/ann2fig/Makefile diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/ann2fig/ann2fig.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/ann2fig/ann2fig.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/ann2fig/ann2fig.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/ann2fig/ann2fig.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/doc/ANNmanual.pdf b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/doc/ANNmanual.pdf similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/doc/ANNmanual.pdf rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/doc/ANNmanual.pdf diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANN.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANN.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANN.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANN.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANNperf.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANNperf.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANNperf.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANNperf.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANNx.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANNx.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANNx.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/include/ANN/ANNx.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/Makefile b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/Makefile similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/Makefile rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/Makefile diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/ann_sample.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/ann_sample.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/ann_sample.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/ann_sample.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/data.pts b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/data.pts similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/data.pts rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/data.pts diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/query.pts b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/query.pts similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/query.pts rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/query.pts diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/sample.save b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/sample.save similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/sample/sample.save rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/sample/sample.save diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/ANN.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/ANN.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/ANN.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/ANN.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/Makefile b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/Makefile similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/Makefile rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/Makefile diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/Makefile.spatialpy b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/Makefile.spatialpy similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/Makefile.spatialpy rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/Makefile.spatialpy diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_fix_rad_search.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_fix_rad_search.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_fix_rad_search.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_fix_rad_search.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_pr_search.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_pr_search.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_pr_search.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_pr_search.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_search.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_search.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_search.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_search.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_tree.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_tree.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_tree.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_tree.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_tree.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_tree.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_tree.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/bd_tree.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/brute.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/brute.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/brute.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/brute.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_dump.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_dump.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_dump.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_dump.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_fix_rad_search.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_fix_rad_search.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_fix_rad_search.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_fix_rad_search.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_fix_rad_search.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_fix_rad_search.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_fix_rad_search.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_fix_rad_search.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_pr_search.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_pr_search.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_pr_search.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_pr_search.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_pr_search.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_pr_search.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_pr_search.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_pr_search.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_search.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_search.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_search.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_search.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_search.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_search.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_search.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_search.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_split.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_split.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_split.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_split.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_split.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_split.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_split.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_split.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_tree.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_tree.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_tree.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_tree.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_tree.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_tree.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_tree.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_tree.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_util.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_util.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_util.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_util.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_util.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_util.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_util.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/kd_util.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/perf.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/perf.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/perf.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/perf.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/pr_queue.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/pr_queue.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/pr_queue.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/pr_queue.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/pr_queue_k.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/pr_queue_k.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/src/pr_queue_k.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/src/pr_queue_k.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/Makefile b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/Makefile similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/Makefile rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/Makefile diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/ann_test.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/ann_test.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/ann_test.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/ann_test.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/rand.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/rand.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/rand.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/rand.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/rand.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/rand.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/rand.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/rand.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test1-data.pts b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test1-data.pts similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test1-data.pts rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test1-data.pts diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test1-query.pts b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test1-query.pts similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test1-query.pts rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test1-query.pts diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test1.in b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test1.in similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test1.in rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test1.in diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test1.save b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test1.save similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test1.save rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test1.save diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test2-data.pts b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test2-data.pts similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test2-data.pts rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test2-data.pts diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test2-query.pts b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test2-query.pts similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test2-query.pts rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test2-query.pts diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test2.in b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test2.in similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test2.in rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test2.in diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test2.save b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test2.save similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/test/test2.save rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/ANN/test/test2.save diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/NRMConstant/README_NRMConstant.txt b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/NRMConstant/README_NRMConstant.txt similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/NRMConstant/README_NRMConstant.txt rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/NRMConstant/README_NRMConstant.txt diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/NRMConstant/demo_NRMConstant_v5.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/NRMConstant/demo_NRMConstant_v5.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/external/NRMConstant/demo_NRMConstant_v5.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/external/NRMConstant/demo_NRMConstant_v5.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/NRMConstant_v5.hpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/NRMConstant_v5.hpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/NRMConstant_v5.hpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/NRMConstant_v5.hpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/count_cores.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/count_cores.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/count_cores.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/count_cores.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/model.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/model.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/model.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/model.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/output.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/output.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/output.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/output.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/particle.hpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/particle.hpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/particle.hpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/particle.hpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/particle_system.hpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/particle_system.hpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/particle_system.hpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/particle_system.hpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/propensities.hpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/propensities.hpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/propensities.hpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/propensities.hpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/pthread_barrier.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/pthread_barrier.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/pthread_barrier.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/pthread_barrier.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/read_lammps_input_file.h b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/read_lammps_input_file.h similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/read_lammps_input_file.h rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/read_lammps_input_file.h diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/simulate.hpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/simulate.hpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/simulate.hpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/simulate.hpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/include/simulate_rdme.hpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/simulate_rdme.hpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/include/simulate_rdme.hpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/include/simulate_rdme.hpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/propensity_file_template.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/propensity_file_template.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/propensity_file_template.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/propensity_file_template.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/NRMConstant_v5.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/NRMConstant_v5.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/NRMConstant_v5.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/NRMConstant_v5.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/count_cores.c b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/count_cores.c similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/count_cores.c rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/count_cores.c diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/model.c b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/model.c similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/model.c rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/model.c diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/output.c b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/output.c similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/output.c rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/output.c diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/particle.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/particle.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/particle.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/particle.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/pthread_barrier.c b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/pthread_barrier.c similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/pthread_barrier.c rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/pthread_barrier.c diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/read_lammps_input_file.c b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/read_lammps_input_file.c similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/read_lammps_input_file.c rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/read_lammps_input_file.c diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/simulate.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/simulate.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/simulate.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/simulate.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/simulate_rdme.cpp b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/simulate_rdme.cpp similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/simulate_rdme.cpp rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/simulate_rdme.cpp diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/src/simulate_threads.c b/spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/simulate_threads.c similarity index 100% rename from spatialpy/ssa_sdpd-c-simulation-engine/src/simulate_threads.c rename to spatialpy/solvers/c_base/ssa_sdpd-c-simulation-engine/src/simulate_threads.c diff --git a/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/lib/.gitkeep b/spatialpy/ssa_sdpd-c-simulation-engine/external/ANN/lib/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/test/run_tests.py b/test/run_tests.py index 5811d5ad..15c8c2b3 100755 --- a/test/run_tests.py +++ b/test/run_tests.py @@ -30,11 +30,17 @@ print('Running tests in develop mode. Appending repository directory to system path.') sys.path.append(os.path.join(os.path.dirname(__file__), '..')) + import test_species + import test_parameter + import test_reaction import test_model import test_solver #import test_mincde modules = [ + test_species, + test_parameter, + test_reaction, test_model, test_solver, #test_mincde, diff --git a/test/test_parameter.py b/test/test_parameter.py new file mode 100644 index 00000000..3d7299b9 --- /dev/null +++ b/test/test_parameter.py @@ -0,0 +1,105 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' +import unittest + +import spatialpy +from spatialpy import Parameter +from spatialpy import ParameterError + +class TestParameter(unittest.TestCase): + ''' + ################################################################################################ + Unit tests for spatialpy.Parameter. + ################################################################################################ + ''' + def test_constructor(self): + """ Test the Parameter constructor. """ + parameter = Parameter(name="test_parameter", expression="0.5") + self.assertEqual(parameter.name, "test_parameter") + self.assertEqual(parameter.expression, "0.5") + + + def test_constructor__no_name(self): + """ Test the Parameter constructor without name. """ + with self.assertRaises(ParameterError): + parameter = Parameter(expression="0.5") + + + def test_constructor__name_not_str(self): + """ Test the Parameter constructor with non-str name. """ + with self.assertRaises(ParameterError): + parameter = Parameter(name=0, expression="0.5") + + + def test_constructor__no_expression(self): + """ Test the Parameter constructor without expression. """ + with self.assertRaises(ParameterError): + parameter = Parameter(name="test_parameter") + + + def test_constructor__int_expression(self): + """ Test the Parameter constructor with int expression. """ + parameter = Parameter(name="test_parameter", expression=1) + self.assertEqual(parameter.expression, "1") + + + def test_constructor__float_expression(self): + """ Test the Parameter constructor with float expression. """ + parameter = Parameter(name="test_parameter", expression=0.5) + self.assertEqual(parameter.expression, "0.5") + + + def test___str___(self): + """ Test Parameter.__str__ method. """ + parameter = Parameter(name="test_parameter", expression="0.5") + self.assertIsInstance(str(parameter), str) + + + def test__evaluate(self): + """ Test Parameter._evaluate method. """ + parameter = Parameter(name="test_parameter", expression="0.5") + parameter._evaluate() + self.assertEqual(parameter.value, 0.5) + + + def test__evaluate__parameter_in_namespace(self): + """ Test Parameter._evaluate method with parameter in namespace. """ + parameter = Parameter(name="test_parameter", expression="k1 + 0.5") + parameter._evaluate(namespace={"k1": 3}) + self.assertEqual(parameter.value, 3.5) + + + def test__evaluate__species_in_namespace(self): + """ Test Parameter._evaluate method with species in namespace. """ + parameter = Parameter(name="test_parameter", expression="S0 + 0.5") + parameter._evaluate(namespace={"S0": 100}) + self.assertEqual(parameter.value, 100.5) + + + def test__evaluate__improper_expression(self): + """ Test Parameter._evaluate method with invalid expression. """ + parameter = Parameter(name="test_parameter", expression="[0.5]") + with self.assertRaises(ParameterError): + parameter._evaluate() + + + def test__evaluate__param_not_in_namespace(self): + """ Test Parameter._evaluate method with arg missing from namespace. """ + parameter = Parameter(name="test_parameter", expression="k1 + 0.5") + with self.assertRaises(ParameterError): + parameter._evaluate() diff --git a/test/test_reaction.py b/test/test_reaction.py new file mode 100644 index 00000000..0d342849 --- /dev/null +++ b/test/test_reaction.py @@ -0,0 +1,168 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' +import unittest + +import spatialpy +from spatialpy import Reaction +from spatialpy import ReactionError + +class TestReaction(unittest.TestCase): + ''' + ################################################################################################ + Unit tests for spatialpy.Reaction. + ################################################################################################ + ''' + def test_constructor__mass_action(self): + """ Test the Reaction constructor for a mass action reaction. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_rate = "k1" + reaction = Reaction(name="test_reaction", reactants=test_reactants, products=test_products, rate=test_rate) + self.assertEqual(reaction.name, "test_reaction") + self.assertEqual(reaction.reactants, test_reactants) + self.assertEqual(reaction.products, test_products) + self.assertEqual(reaction.rate, test_rate) + + + def test_constructor__custom_propensity(self): + """ Test the Reaction constructor for a custom propensity reaction. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_propensity = "k1 * A * B" + reaction = Reaction( + name="test_reaction", reactants=test_reactants, products=test_products, propensity_function=test_propensity + ) + self.assertEqual(reaction.name, "test_reaction") + self.assertEqual(reaction.reactants, test_reactants) + self.assertEqual(reaction.products, test_products) + self.assertEqual(reaction.propensity_function, test_propensity) + + + def test_constructor__name_not_str(self): + """ Test the Reaction constructor with non-str name. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_rate = "k1" + with self.assertRaises(ReactionError): + reaction = Reaction(name=0, reactants=test_reactants, products=test_products, rate=test_rate) + + + def test_constructor__reactants_not_dict(self): + """ Test the Reaction constructor with non-dict reactants. """ + test_reactants = [["C", 1]] + test_products = {"A": 1, "B": 1} + test_rate = "k2" + with self.assertRaises(ReactionError): + reaction = Reaction(name="test_reaction", reactants=test_reactants, products=test_products, rate=test_rate) + + def test_constructor__products_not_dict(self): + """ Test the Reaction constructor with non-dict products. """ + test_reactants = {"A": 1, "B": 1} + test_products = [["C", 1]] + test_rate = "k1" + with self.assertRaises(ReactionError): + reaction = Reaction(name="test_reaction", reactants=test_reactants, products=test_products, rate=test_rate) + + + def test_constructor__propensity_function_not_accepted_type(self): + """ Test the Reaction constructor with a propensity function that is not of the proper type. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_propensity = ["k1 * A * B"] + with self.assertRaises(ReactionError): + reaction = Reaction( + name="test_reaction", reactants=test_reactants, products=test_products, propensity_function=test_propensity + ) + + + def test_constructor__int_propensity_function(self): + """ Test the Reaction constructor with an int propensity function. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_propensity = 20 + reaction = Reaction( + name="test_reaction", reactants=test_reactants, products=test_products, propensity_function=test_propensity + ) + self.assertIsInstance(reaction.propensity_function, str) + self.assertEqual(reaction.propensity_function, "20") + + + def test_constructor__float_propensity_function(self): + """ Test the Reaction constructor with a float propensity function. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_propensity = 0.5 + reaction = Reaction( + name="test_reaction", reactants=test_reactants, products=test_products, propensity_function=test_propensity + ) + self.assertIsInstance(reaction.propensity_function, str) + self.assertEqual(reaction.propensity_function, "0.5") + + + def test_constructor__rate_not_accepted_type(self): + """ Test the Reaction constructor with a rate that is not of the proper type. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_rate = ["k1"] + with self.assertRaises(ReactionError): + reaction = Reaction(name="test_reaction", reactants=test_reactants, products=test_products, rate=test_rate) + + + def test_constructor__int_rate(self): + """ Test the Reaction constructor with an int rate. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_rate = 20 + reaction = Reaction(name="test_reaction", reactants=test_reactants, products=test_products, rate=test_rate) + self.assertIsInstance(reaction.rate, str) + self.assertEqual(reaction.rate, "20") + + + def test_constructor__float_rate(self): + """ Test the Reaction constructor with a float rate. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_rate = 0.5 + reaction = Reaction(name="test_reaction", reactants=test_reactants, products=test_products, rate=test_rate) + self.assertIsInstance(reaction.rate, str) + self.assertEqual(reaction.rate, "0.5") + + + def test_constructor__restrict_to_not_accepted_type(self): + """ Test the Reaction constructor with a restrict_to that is not the proper type. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_rate = "k1" + test_restrict_to = "1" + with self.assertRaises(ReactionError): + reaction = Reaction( + name="test_reaction", reactants=test_reactants, products=test_products,rate=test_rate, restrict_to=test_restrict_to + ) + + + def test_constructor__int_restrict_to(self): + """ Test the Reaction constructor with a int restrict_to. """ + test_reactants = {"A": 1, "B": 1} + test_products = {"C": 1} + test_rate = "k1" + test_restrict_to = 1 + reaction = Reaction( + name="test_reaction", reactants=test_reactants, products=test_products,rate=test_rate, restrict_to=test_restrict_to + ) + self.assertIsInstance(reaction.restrict_to, list) + self.assertEqual(reaction.restrict_to, [1]) diff --git a/test/test_solver.py b/test/test_solver.py index 61c23bf4..53817a09 100644 --- a/test/test_solver.py +++ b/test/test_solver.py @@ -25,7 +25,7 @@ import unittest import numpy import spatialpy -from spatialpy.expression import Expression, ExpressionConverter +from spatialpy.solvers.build.expression import Expression, ExpressionConverter class diffusion_debug(spatialpy.Model): diff --git a/test/test_species.py b/test/test_species.py new file mode 100644 index 00000000..588c14fe --- /dev/null +++ b/test/test_species.py @@ -0,0 +1,91 @@ +''' +SpatialPy is a Python 3 package for simulation of +spatial deterministic/stochastic reaction-diffusion-advection problems +Copyright (C) 2021 SpatialPy developers. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU GENERAL PUBLIC LICENSE Version 3 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU GENERAL PUBLIC LICENSE Version 3 for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +''' +import unittest + +import spatialpy +from spatialpy import Species +from spatialpy import SpeciesError + +class TestSpecies(unittest.TestCase): + ''' + ################################################################################################ + Unit tests for spatialpy.Species. + ################################################################################################ + ''' + def test_constructor(self): + """ Test the Species constructor. """ + species = Species(name="test_species", diffusion_coefficient=0) + self.assertEqual(species.name, "test_species") + self.assertEqual(species.diffusion_coefficient, 0) + + + def test_constructor__no_name(self): + """ Test the Species constructor without name. """ + with self.assertRaises(SpeciesError): + species = Species(diffusion_coefficient=0) + + + def test_constructor__name_not_str(self): + """ Test the Species constructor with non-str name. """ + with self.assertRaises(SpeciesError): + species = Species(name=0, diffusion_coefficient=0) + + + def test_constructor__no_diffusion_coefficient(self): + """ Test the Species constructor without diffusion_coefficient. """ + with self.assertRaises(SpeciesError): + species = Species(name="test_species") + + + def test_constructor__negative_diffusion_coefficient(self): + """ Test the Species constructor with negative diffusion_coefficient. """ + with self.assertRaises(SpeciesError): + species = Species(name="test_species", diffusion_coefficient=-1) + + + def test_constructor__diffusion_coefficient_not_int_or_float(self): + """ Test the Species constructor with non-int or non-float diffusion_coefficient. """ + with self.assertRaises(SpeciesError): + species = Species(name="test_species", diffusion_coefficient="0") + + + def test___str___(self): + """ Test Species.__str__ method. """ + species = Species(name="test_species", diffusion_coefficient=0) + self.assertIsInstance(str(species), str) + + + def test_set_diffusion_coefficient(self): + """ Test Species.set_diffusion_coefficient method. """ + species = Species(name="test_species", diffusion_coefficient=0) + species.set_diffusion_coefficient(diffusion_coefficient=1) + self.assertEqual(species.diffusion_coefficient, 1) + + + def test_set_diffusion_coefficient__negative_diffusion_coefficient(self): + """ Test Species.set_diffusion_coefficient method with negative diffusion_coefficient. """ + species = Species(name="test_species", diffusion_coefficient=0) + with self.assertRaises(SpeciesError): + species.set_diffusion_coefficient(diffusion_coefficient=-1) + + + def test_set_diffusion_coefficient__diffusion_coefficient_not_int_or_float(self): + """ Test Species.set_diffusion_coefficient method with non-int or non-float diffusion_coefficient. """ + species = Species(name="test_species", diffusion_coefficient=0) + with self.assertRaises(SpeciesError): + species.set_diffusion_coefficient(diffusion_coefficient="1")