diff --git a/debugging_script.py b/debugging_script.py index 18dc6f5..1aa2c97 100644 --- a/debugging_script.py +++ b/debugging_script.py @@ -1,4 +1,6 @@ from test_script import * +from test_ode_syntax import * +import sys # This is here because pytest is too slow - so it's a faster test that avoid the collection step. # Pytest is used in the GitHub while pushing @@ -30,8 +32,8 @@ test_conditional_between_meta_species, test_conditional_between_meta_species_2, test_event_reaction_not_allowed, - all_test, - all_test_2, + test_all, + test_2_all, test_error_mult, test_set_counts, test_bool_error, @@ -45,11 +47,10 @@ test_string_events_assignment, test_plotting, test_volume_after_sim, - test_parameters_with_sbml, test_shared_parameter_name, test_set_counts_parameters, test_repeated_parameters, - initial_expression_test, + test_initial_expression, test_wrong_dimension_error, test_more_than_used, zero_rate_test, @@ -105,6 +106,14 @@ test_2D_reaction_with_units, test_parameters_as_initial_values, test_parameters_in_lambda_expression, + test_ode_syntax_basic, + test_ode_syntax_two_species, + test_ode_applied_to_species, + test_ode_neg_test, + test_ode_compartments, + test_ode_complex_expressions, + test_ode_inheritance, + test_ode_with_functions ] # test_no_species_in_asg @@ -117,14 +126,22 @@ # sub_test = [test_parameters_with_sbml] def perform_tests(): any_failed = False + failed_tests = [] for test in sub_test: try: test() print(f"Test {test} passed") except: - print("\033[91m" + f"Test {test} failed" + "\033[0m", file=sys.stderr) + message = "\033[91m" + f"Test {test} failed" + "\033[0m" + print(message, file=sys.stderr) any_failed = True + failed_tests.append(message) if any_failed: + print() + print("The following tests failed", file=sys.stderr) + for t in failed_tests: + print(t, file=sys.stderr) + assert False diff --git a/for_local_use.py b/for_local_use.py index af001a9..f6d8fcb 100644 --- a/for_local_use.py +++ b/for_local_use.py @@ -1,12 +1,10 @@ from mobspy import * +from mobspy.modules.ode_operator import dt +from mobspy.modules.functions import ms_exp +from testutils import compare_model, compare_model_ignore_order -if __name__ == "__main__": - A, B = BaseSpecies() - A.a1, A.a2, A.a3, B.b1, B.b2 - C = A*B - ~C.a1 >> Zero [10] +if __name__ == "__main__": - S = Simulation(A | C) - print(S.compile()) \ No newline at end of file + pass \ No newline at end of file diff --git a/mobspy/modules/assignments_implementation.py b/mobspy/modules/assignments_implementation.py index cc38189..19e1270 100644 --- a/mobspy/modules/assignments_implementation.py +++ b/mobspy/modules/assignments_implementation.py @@ -52,47 +52,61 @@ def check_context(self): @staticmethod def check_arguments(first, second): + spe_list_first = [] + spe_list_second = [] + + # Only extract species if it's actually a Species or Reacting_Species + if hasattr(first, 'get_spe_object'): + spe_list_first = [first] + + if hasattr(second, 'get_spe_object'): + spe_list_second = [second] + if type(first) == int or type(first) == float: first = mbe_MobsPyExpression( str(first), - None, + species_object= None, dimension=None, count_in_model=True, concentration_in_model=False, count_in_expression=False, concentration_in_expression=False, + species_list_operation_order=spe_list_first ) if type(second) == int or type(second) == float: second = mbe_MobsPyExpression( str(second), - None, + species_object= None, dimension=None, count_in_model=True, concentration_in_model=False, count_in_expression=False, concentration_in_expression=False, + species_list_operation_order= spe_list_second ) if not isinstance(first, mbe_MobsPyExpression): first = mbe_MobsPyExpression( "($asg_" + str(first) + ")", - None, + species_object=None, dimension=None, count_in_model=True, concentration_in_model=False, count_in_expression=False, concentration_in_expression=False, + species_list_operation_order=spe_list_first ) if not isinstance(second, mbe_MobsPyExpression): second = mbe_MobsPyExpression( "($asg_" + str(second) + ")", - None, + species_object=None, dimension=None, count_in_model=True, concentration_in_model=False, count_in_expression=False, concentration_in_expression=False, + species_list_operation_order=spe_list_second ) return first, second diff --git a/mobspy/modules/event_functions.py b/mobspy/modules/event_functions.py index 266e761..d1e6c9e 100644 --- a/mobspy/modules/event_functions.py +++ b/mobspy/modules/event_functions.py @@ -12,6 +12,7 @@ search_for_parameters_in_str as frc_search_for_parameters_in_str, ) +# @TODO remove search parameters in string - don't use it anymore - slowly deprecate this function def format_event_dictionary_for_sbml( species_for_sbml, diff --git a/mobspy/modules/function_rate_code.py b/mobspy/modules/function_rate_code.py index 0b00369..e4832c3 100644 --- a/mobspy/modules/function_rate_code.py +++ b/mobspy/modules/function_rate_code.py @@ -110,11 +110,6 @@ def extract_reaction_rate( # Remove dollar sign symbols from expression object reaction_rate_string = reaction_rate_string.replace("$", "") - if parameter_exist: - parameters_in_reaction = search_for_parameters_in_str( - reaction_rate_string, parameter_exist, parameters_in_reaction - ) - elif isinstance(rate, mbe_MobsPyExpression): # Having an expression variable implies it is a constructed expression - not mass action if len(rate._expression_variables) > 0: @@ -162,9 +157,6 @@ def extract_reaction_rate( + str(reactant_string_list) ) elif type(reaction_rate_function) == str: - parameters_in_reaction = search_for_parameters_in_str( - reaction_rate_function, parameter_exist, parameters_in_reaction - ) reaction_rate_string = reaction_rate_function else: simlog_debug(type(reaction_rate_function)) @@ -303,6 +295,7 @@ def prepare_arguments_for_callable( return argument_dict +# @TODO this function needs to be deprecated - Please slowly remove form the code def search_for_parameters_in_str( reaction_rate_string, parameters_exist, parameters_in_reaction ): diff --git a/mobspy/modules/functions.py b/mobspy/modules/functions.py new file mode 100644 index 0000000..1e20fc9 --- /dev/null +++ b/mobspy/modules/functions.py @@ -0,0 +1,98 @@ +from mobspy.modules.meta_class import Species, Reacting_Species +from mobspy.modules.mobspy_expressions import MobsPyExpression, OverrideQuantity +from mobspy.modules.assignments_implementation import Assign +from mobspy.simulation_logging.log_scripts import error as simlog_error +import math + +class MathFunctionWrapper: + """Wrapper for mathematical functions that work with MobsPy expressions + + Note for future developers. To add units to these functions, + use the unit compilation to check if dealing with concentrations or counts + + The units must be compiled inside a function as the input is non-dimensional. + Therefore, the unit compilation must be performed here at this step + + Currently, for the ODE application, I don't need units, + so I need to leave this for future needs + """ + + def __init__(self, name): + self.name = name # COPASI function name: 'exp', 'sin', 'cos', etc. + + def _create_expression(self, expression, new_operation): + """Create new MobsPyExpression with this function applied.""" + return MobsPyExpression( + species_string="$Null", + species_object=None, + operation=new_operation, + unit_count_op=1, + unit_conc_op=1, + dimension=expression._dimension, + expression_variables=set(expression._expression_variables), + parameter_set=set(expression._parameter_set), + count_in_model=expression._count_in_model, + concentration_in_model=expression._concentration_in_model, + count_in_expression=expression._count_in_expression, + concentration_in_expression=expression._concentration_in_expression, + has_units=expression._has_units, + species_list_operation_order=list(expression.species_list_operation_order) + ) + + def __call__(self, expression): + + if not Assign.check_context(): + simlog_error( + "The expression functions must only be called " + ) + + # MobsPy Expressions + if isinstance(expression, MobsPyExpression): + + if expression._has_units == 'T': + simlog_error('At this current version, MobsPy functions do not support ') + + new_operation = f"{self.name}({expression._operation})" + return self._create_expression(expression, new_operation) + + # Species passed + elif ((isinstance(expression, Species) or isinstance(expression, Reacting_Species)) + and Assign.check_context()): + + if isinstance(expression, Reacting_Species): + if len(expression.list_of_reactants) > 1: + simlog_error( + message="Reacting species with multiple reactants should not be applied to a function", + full_exception_log=True + ) + + expression = Assign.mul(1, expression) + new_operation = f"{self.name}({expression._operation})" + return self._create_expression(expression, new_operation) + + else: + simlog_error( + message="MobsPy functions were called on a non-valid context", + full_exception_log=True + ) + + + +# Create all the COPASI-compatible math functions +# Please add new functions with ms to avoid conflict with Python's existing modules +ms_exp = MathFunctionWrapper('exp') +ms_logn = MathFunctionWrapper('log') +ms_log10 = MathFunctionWrapper('log10') +ms_sin = MathFunctionWrapper('sin') +ms_cos = MathFunctionWrapper('cos') +ms_tan = MathFunctionWrapper('tan') +ms_asin = MathFunctionWrapper('asin') +ms_acos = MathFunctionWrapper('acos') +ms_atan = MathFunctionWrapper('atan') +ms_sinh = MathFunctionWrapper('sinh') +ms_cosh = MathFunctionWrapper('cosh') +ms_tanh = MathFunctionWrapper('tanh') +ms_floor = MathFunctionWrapper('floor') +ms_ceil = MathFunctionWrapper('ceil') +ms_abs = MathFunctionWrapper('abs') +ms_sqrt = MathFunctionWrapper('sqrt') \ No newline at end of file diff --git a/mobspy/modules/logic_operator_objects.py b/mobspy/modules/logic_operator_objects.py index b48f19d..8db068c 100644 --- a/mobspy/modules/logic_operator_objects.py +++ b/mobspy/modules/logic_operator_objects.py @@ -215,7 +215,7 @@ def __eq__(self, other): else: return id(self) == id(other) - def __neg__(self, other): + def __ne__(self, other): return id(self) != id(other) def __hash__(self): diff --git a/mobspy/modules/meta_class.py b/mobspy/modules/meta_class.py index 75da10f..14561d7 100644 --- a/mobspy/modules/meta_class.py +++ b/mobspy/modules/meta_class.py @@ -547,6 +547,13 @@ def __radd__(self, other): def __invert__(self): return self.c('not$') + def __neg__(self): + if asgi_Assign.check_context(): + return asgi_Assign.mul(-1, self) + else: + simlog_error("The negative operator was applied to a Reacting Species in the wrong context") + + def __rshift__(self, other): """ The >> operator for defining reactions. It passes two instances of reacting species to construct the @@ -1100,6 +1107,12 @@ def __radd__(self, other): def __invert__(self): return self.c('not$') + def __neg__(self): + if asgi_Assign.check_context(): + return asgi_Assign.mul(-1, self) + else: + simlog_error("The negative operator was applied to a Species in the wrong context") + @classmethod def _compile_defined_reaction(cls, code_line, line_number): # Check that line ends with a ']' and after that only ')', ',', whitespace and comments @@ -1110,10 +1123,18 @@ def _compile_defined_reaction(cls, code_line, line_number): if re.search(set_pattern, code_line): return True + # If line doesn't contain '>>', it's a multiline reaction, so we don't parse it + if '>>' not in code_line: + return True + # Make sure the reaction rate is present. # If code_line ends with '>>' it might be a multiline reaction, so skip validation if code_line.rstrip().endswith('>>'): return True + + # If code_line ends with '[' it's a multiline rate, skip validation + if code_line.rstrip().endswith('['): + return True if not bool(re.search(pattern, code_line)): simlog_error( diff --git a/mobspy/modules/mobspy_expressions.py b/mobspy/modules/mobspy_expressions.py index 80e608a..bd58236 100644 --- a/mobspy/modules/mobspy_expressions.py +++ b/mobspy/modules/mobspy_expressions.py @@ -361,6 +361,14 @@ def __rpow__(self, other): else: return self.non_expression_rpow(other) + def __neg__(self): + if self._ms_active: + other = check_if_non_expression_operated(-1) + count_op, conc_op = self.execute_quantity_op(-1, "__mul__") + return self.create_from_new_operation(other, "*", count_op, conc_op, False) + else: + return self.non_expression_neg() + def combine_binary_attributes(self, other, attribute): """ Or gates to binary True or False attributes from self and other @@ -418,6 +426,8 @@ def _generate_necessary_attributes(self): # Dimension self._dimension = None + self.species_list_operation_order = [] + def create_from_new_operation( self, other, symbol, count_op, conc_op, direct_sense=True, operation=None ): @@ -514,6 +524,15 @@ def create_from_new_operation( except AttributeError: pass + # Accumulate species in operation order + new_species_list_operation_order = list(getattr(self, 'species_list_operation_order', [])) + try: + for spe in other.species_list_operation_order: + if spe not in new_species_list_operation_order: + new_species_list_operation_order.append(spe) + except AttributeError: + pass + if isinstance(other, ExpressionDefiner): new_parameter_set = self._parameter_set.union(other._parameter_set) elif isinstance(other, MobsPyExpression): @@ -579,6 +598,7 @@ def create_from_new_operation( count_in_expression=_count_in_expression, concentration_in_expression=_concentration_in_expression, has_units=_has_units, + species_list_operation_order=new_species_list_operation_order ) @@ -848,10 +868,16 @@ def __init__( count_in_expression=True, concentration_in_expression=False, has_units=False, + species_list_operation_order = None ): super().__init__(species_string, species_object) self._generate_necessary_attributes() + if species_list_operation_order is None: + self.species_list_operation_order = [] + else: + self.species_list_operation_order = species_list_operation_order + self._ms_active = True self._dimension = dimension @@ -1053,6 +1079,7 @@ def check_if_non_expression_operated(other): concentration_in_model=False, count_in_expression=False, concentration_in_expression=False, + species_list_operation_order= [other] if hasattr(other, 'get_spe_object') else [] ) return other diff --git a/mobspy/modules/mobspy_parameters.py b/mobspy/modules/mobspy_parameters.py index aa9ba07..c00c543 100644 --- a/mobspy/modules/mobspy_parameters.py +++ b/mobspy/modules/mobspy_parameters.py @@ -14,7 +14,6 @@ class Internal_Parameter_Constructor(me_ExpressionDefiner, me_QuantityConverter) """ # convert_received_unit - parameter_stack = {} def __init__(self, name, value): diff --git a/mobspy/modules/ode_operator.py b/mobspy/modules/ode_operator.py new file mode 100644 index 0000000..21ed912 --- /dev/null +++ b/mobspy/modules/ode_operator.py @@ -0,0 +1,112 @@ +from mobspy.modules.assignments_implementation import Assign +from mobspy.modules.meta_class import Species, Reacting_Species +from mobspy.simulation_logging.log_scripts import error as simlog_error +import re +from inspect import stack as inspect_stack + + +def generate_ODE_reaction_rate(list_of_used_species, expression): + """Generates a rate function from the ODE expression.""" + expr_string = str(expression._operation) + + # Convert $asg_X to $pos_N based on position in list + for i, spe in enumerate(list_of_used_species): + spe_name = str(spe) + expr_string = expr_string.replace(f"($asg_{spe_name})", f"$_pos_{i}") + + # Create the parameter argument r1, r2, r3 + n = len(list_of_used_species) + param_names = [f"r{i + 1}" for i in range(n)] + param_str = ", ".join(param_names) + + # Build replacement logic + func_code = f"def rate_fn({param_str}):\n\t" + func_code += f"result = {repr(expr_string)}\n\t" + + replace_lines = "" + for i in range(n): + replace_lines += f'result = result.replace("$_pos_{i}", str({param_names[i]}))\n\t' + func_code += replace_lines + func_code += "return result" + + local_vars = {} + exec(func_code, {}, local_vars) + return local_vars["rate_fn"] + + +class ODEBinding: + """Intermediate object returned by dt[A] that waits for >> expression.""" + + def __init__(self, state_variable): + self.state_variable = state_variable + + def __rshift__(self, expression): + if isinstance(expression, Reacting_Species): + if len(expression.list_of_reactants) > 1: + simlog_error( + message = "ODE expressions must be built within the dt[...] >> context.\n" + "Expressions like 'C = A + B' followed by 'dt[X] >> C' are not valid.\n" + "Use: dt[X] >> A + B", + full_exception_log = True + ) + + if isinstance(expression, Species) or isinstance(expression, Reacting_Species): + expression = Assign.mul(1, expression) + + Assign.reset_context() + + species_list_operation_order = expression.species_list_operation_order + rate_fn = generate_ODE_reaction_rate( + expression.species_list_operation_order, expression + ) + + reactants = None + for spe in species_list_operation_order: + if reactants is None: + reactants = spe + else: + reactants = reactants + spe + + reactants >> self.state_variable + reactants [rate_fn] + # Returns the reaction, even though it doesn't matter + return self + + +class DifferentialOperator: + """Differential operator for ODE syntax: dt[A] >> expression.""" + + @staticmethod + def _compile_ode_syntax(code_line, line_number): + """Validate that ODE syntax uses >> operator.""" + """Validate that ODE syntax uses >> operator.""" + # Check that dt[...] is followed by >> + if not re.search(r'dt\s*\[.*\]\s*>>', code_line): + simlog_error( + f"At: {code_line}\n" + f"Line number: {line_number}\n" + "ODE syntax requires '>>' operator right after dt[Species] in the same line\n" + "Use: dt[Species] >> expression" + ) + + def __setitem__(self, key, value): + simlog_error( + message = "ODE syntax requires '>>' operator, not '=', right after dt[Species] in the same line\n" + "Use: dt[Species] >> expression", + full_exception_log = True + ) + + def __getitem__(self, item): + if isinstance(item, Species) or isinstance(item, Reacting_Species): + stack_frame = inspect_stack()[1] + code_line = stack_frame.code_context[0] if stack_frame.code_context else "" + line_number = stack_frame.lineno + + self._compile_ode_syntax(code_line, line_number) + + Assign.set_context() # Turn ON before expression is evaluated + return ODEBinding(item) + else: + simlog_error("MobsPy ODE object must only be applied on a species") + + +dt = DifferentialOperator() \ No newline at end of file diff --git a/mobspy/modules/order_operators.py b/mobspy/modules/order_operators.py index 784d0e5..b8fcb79 100644 --- a/mobspy/modules/order_operators.py +++ b/mobspy/modules/order_operators.py @@ -160,15 +160,13 @@ def __call__( for spe_obe in model: if species in spe_obe.get_references(): species_is_referenced_by.append(spe_obe) + all_strings = self.find_all_string_references_to_born_species( + species_is_referenced_by, + characteristics, + ref_characteristics_to_object, + ) products.append( - ( - stoichiometry, - self.find_all_string_references_to_born_species( - species_is_referenced_by, - characteristics, - ref_characteristics_to_object, - ), - ) + [(stoichiometry, s) for s in all_strings] ) continue diff --git a/mobspy/modules/reaction_construction_nb.py b/mobspy/modules/reaction_construction_nb.py index 1e1674e..82ff983 100644 --- a/mobspy/modules/reaction_construction_nb.py +++ b/mobspy/modules/reaction_construction_nb.py @@ -350,6 +350,7 @@ def create_all_reactions( for reactant_string_list in iterator_for_combinations( reactant_species_string_combination_list ): + product_object_list = construct_product_structure(reaction) order_structure = construct_order_structure( base_species_order, reactant_string_list diff --git a/test_ode_syntax.py b/test_ode_syntax.py new file mode 100644 index 0000000..259b2b7 --- /dev/null +++ b/test_ode_syntax.py @@ -0,0 +1,116 @@ +from mobspy.modules.mobspy_parameters import Internal_Parameter_Constructor +from mobspy.modules.ode_operator import dt +from mobspy import * +from testutils import compare_model, compare_model_ignore_order +from mobspy.modules.functions import ms_exp + +def test_ode_syntax_basic(): + A = BaseSpecies() + + dt[A] >> -0.1 * A + + A(100) + S = Simulation(A) + assert compare_model(S.compile(), "test_tools/model_ode_syntax_basic.txt") + + +def test_ode_syntax_two_species(): + """ODE with two species: dA/dt = -0.1*A + 0.05*B""" + A, B = BaseSpecies() + + dt[A] >> -0.1 * A + 0.05 * B + + A(100), B(50) + S = Simulation(A | B) + assert compare_model(S.compile(), "test_tools/model_ode_syntax_two_species.txt") + +def test_ode_applied_to_species(): + + A = BaseSpecies() + B = BaseSpecies() + B.b1 + + dt[A] >> A + dt[B.b1] >> B.b1 + + S = Simulation(A | B) + assert compare_model(S.compile(), "test_tools/model_ode_applied_to_species.txt") + + +def test_ode_neg_test(): + + Neg, NegR = BaseSpecies() + NegR.comp1 + + dt[Neg] >> -Neg + dt[NegR] >> -NegR.comp1 + + S = Simulation(Neg | NegR) + assert compare_model(S.compile(), "test_tools/model_ode_neg_test.txt") + + +def test_ode_compartments(): + + """ODE combined with regular CRN reactions""" + A = BaseSpecies() + A.c1, A.c2 + + Zero >> A.c1[1] + A.c1 >> A.c2[1] + + # ODE for A + dt[A] >> -0.1 * A + + S = Simulation(A) + assert compare_model(S.compile(), "test_tools/model_ode_compartments.txt") + + +def test_ode_complex_expressions(): + + """ODE with various complex expressions: Hill functions, Michaelis-Menten, feedback loops""" + A, B, C, D = BaseSpecies() + + # Hill-style repression: production inhibited by B + dt[A] >> 100 / (1 + B ** 2) - 0.1 * A + + # Michaelis-Menten with multiple substrates + dt[B] >> (A * C) / (10 + A + C) - B / (5 + B) + + # Nested fractions and mixed operations + dt[C] >> (A / (1 + A)) * (B / (1 + B)) - 0.05 * C * D + + # Complex feedback with powers and sums + dt[D] >> (A ** 2 + B ** 2) / (100 + A ** 2 + B ** 2) * (1 - D / 1000) + + A(10), B(10), C(10), D(10) + S = Simulation(A | B | C | D) + assert compare_model(S.compile(), "test_tools/model_ode_complex_expressions.txt") + + +def test_ode_inheritance(): + + """ODE applied to parent affects all children""" + Mortal = BaseSpecies() + Human, Animal = New(Mortal) + + # Decay applied to Mortal affects both Human and Animal + dt[Mortal] >> -0.1 * Mortal + + Human(100), Animal(50) + S = Simulation(Human | Animal) + assert compare_model(S.compile(), "test_tools/model_ode_inheritance.txt") + + +def test_ode_with_functions(): + A = BaseSpecies() + + dt[A] >> 1 / (1 + ms_exp(A / 1000)) + + A(100) + S = Simulation(A) + assert compare_model(S.compile(), "test_tools/model_ode_with_functions.txt") + + + + + diff --git a/test_script.py b/test_script.py index a13afba..0ff7029 100644 --- a/test_script.py +++ b/test_script.py @@ -587,7 +587,7 @@ def test_event_reaction_not_allowed(): assert True -def all_test(): +def test_all(): A, B = BaseSpecies() A.a1, A.a2 B.b1, B.b2 @@ -602,7 +602,7 @@ def all_test(): assert compare_model(S.compile(), "test_tools/model_21.txt") -def all_test_2(): +def test_2_all(): B = BaseSpecies() B.b1, B.b2 C, D = New(B) @@ -853,7 +853,9 @@ def test_volume_after_sim(): S.run(plot_data = False) assert int(S.fres[A][-1]) == 42 - +# @TODO Deactivated for now - Searching for parameters in strings is causing troubles +# @TODO Break this text apart - to much in one test +''' def order_model_str(data_for_sbml): species_for_sbml = data_for_sbml["species_for_sbml"] mappings_for_sbml = data_for_sbml["mappings"] @@ -916,7 +918,7 @@ def test_parameters_with_sbml(): A.a1, A.a2 a, b, c, d, f, h = ModelParameters([1, 2], [1, 2], [1, 2], [1, 2], [1, 2], [1, 2]) - A >> 2 * A[lambda: "5*(b + c)/10"] + A >> 2 * A [lambda: 5*(b + c)/10] All[A](a) S1 = Simulation(A) @@ -951,7 +953,7 @@ def test_parameters_with_sbml(): model_str += order_model_str(data_for_sbml) assert compare_model_ignore_order(model_str, "test_tools/model_31.txt") - +''' def test_shared_parameter_name(): try: @@ -1014,7 +1016,7 @@ def test_repeated_parameters(): assert True -def initial_expression_test(): +def test_initial_expression(): A, B, Hey = BaseSpecies() D = New(A) diff --git a/test_tools/model_ode_applied_to_species.txt b/test_tools/model_ode_applied_to_species.txt new file mode 100644 index 0000000..9b9ebba --- /dev/null +++ b/test_tools/model_ode_applied_to_species.txt @@ -0,0 +1,17 @@ + +Species +A,0 +B.b1,0 + +Mappings +A : +A +B : +B.b1 + +Parameters +volume,1 + +Reactions +reaction_0,{'re': [(1, 'A')], 'pr': [(2, 'A')], 'kin': '(1*A)'} +reaction_1,{'re': [(1, 'B.b1')], 'pr': [(2, 'B.b1')], 'kin': '(1*B.b1)'} diff --git a/test_tools/model_ode_compartments.txt b/test_tools/model_ode_compartments.txt new file mode 100644 index 0000000..09cebcb --- /dev/null +++ b/test_tools/model_ode_compartments.txt @@ -0,0 +1,18 @@ + +Species +A.c1,0 +A.c2,0 + +Mappings +A : +A.c1 +A.c2 + +Parameters +volume,1 + +Reactions +reaction_0,{'re': [(1, 'A.c1')], 'pr': [(1, 'A.c2')], 'kin': 'A.c1 * 1'} +reaction_1,{'re': [(1, 'A.c1')], 'pr': [(2, 'A.c1')], 'kin': '(-0.1*A.c1)'} +reaction_2,{'re': [(1, 'A.c2')], 'pr': [(2, 'A.c2')], 'kin': '(-0.1*A.c2)'} +reaction_3,{'re': [], 'pr': [(1, 'A.c1')], 'kin': '1 * volume'} diff --git a/test_tools/model_ode_complex_expressions.txt b/test_tools/model_ode_complex_expressions.txt new file mode 100644 index 0000000..64a0581 --- /dev/null +++ b/test_tools/model_ode_complex_expressions.txt @@ -0,0 +1,26 @@ + +Species +A,10 +B,10 +C,10 +D,10 + +Mappings +A : +A +B : +B +C : +C +D : +D + +Parameters +volume,1 + +Reactions +reaction_0,{'re': [(1, 'A'), (1, 'B'), (1, 'C'), (1, 'D')], 'pr': [(2, 'C'), (1, 'A'), (1, 'B'), (1, 'D')], 'kin': '(((A/(1+A))*(B/(1+B)))-((0.05*C)*D))'} +reaction_1,{'re': [(1, 'A'), (1, 'B'), (1, 'D')], 'pr': [(2, 'D'), (1, 'A'), (1, 'B')], 'kin': '((((A^2)+(B^2))/((100+(A^2))+(B^2)))*(1-(D/1000)))'} +reaction_2,{'re': [(1, 'A'), (1, 'C'), (1, 'B')], 'pr': [(2, 'B'), (1, 'A'), (1, 'C')], 'kin': '(((A*C)/((10+A)+C))-(B/(5+B)))'} +reaction_3,{'re': [(1, 'B'), (1, 'A')], 'pr': [(2, 'A'), (1, 'B')], 'kin': '((100/(1+(B^2)))-(0.1*A))'} + diff --git a/test_tools/model_ode_inheritance.txt b/test_tools/model_ode_inheritance.txt new file mode 100644 index 0000000..dcf0f7a --- /dev/null +++ b/test_tools/model_ode_inheritance.txt @@ -0,0 +1,18 @@ + +Species +Animal,50 +Human,100 + +Mappings +Animal : +Animal +Human : +Human + +Parameters +volume,1 + +Reactions +reaction_0,{'re': [(1, 'Animal')], 'pr': [(2, 'Animal')], 'kin': '(-0.1*Animal)'} +reaction_1,{'re': [(1, 'Human')], 'pr': [(2, 'Human')], 'kin': '(-0.1*Human)'} + diff --git a/test_tools/model_ode_neg_test.txt b/test_tools/model_ode_neg_test.txt new file mode 100644 index 0000000..3f60c99 --- /dev/null +++ b/test_tools/model_ode_neg_test.txt @@ -0,0 +1,18 @@ + +Species +Neg,0 +NegR.comp1,0 + +Mappings +Neg : +Neg +NegR : +NegR.comp1 + +Parameters +volume,1 + +Reactions +reaction_0,{'re': [(1, 'Neg')], 'pr': [(2, 'Neg')], 'kin': '(-1*Neg)'} +reaction_1,{'re': [(1, 'NegR.comp1')], 'pr': [(2, 'NegR.comp1')], 'kin': '(-1*NegR.comp1)'} + diff --git a/test_tools/model_ode_syntax_basic.txt b/test_tools/model_ode_syntax_basic.txt new file mode 100644 index 0000000..4e880a1 --- /dev/null +++ b/test_tools/model_ode_syntax_basic.txt @@ -0,0 +1,13 @@ + +Species +A,100 + +Mappings +A : +A + +Parameters +volume,1 + +Reactions +reaction_0,{'re': [(1, 'A')], 'pr': [(2, 'A')], 'kin': '(-0.1*A)'} diff --git a/test_tools/model_ode_syntax_two_species.txt b/test_tools/model_ode_syntax_two_species.txt new file mode 100644 index 0000000..1e76d2a --- /dev/null +++ b/test_tools/model_ode_syntax_two_species.txt @@ -0,0 +1,16 @@ + +Species +A,100 +B,50 + +Mappings +A : +A +B : +B + +Parameters +volume,1 + +Reactions +reaction_0,{'re': [(1, 'A'), (1, 'B')], 'pr': [(2, 'A'), (1, 'B')], 'kin': '((-0.1*A)+(0.05*B))'} diff --git a/test_tools/model_ode_with_functions.txt b/test_tools/model_ode_with_functions.txt new file mode 100644 index 0000000..1304793 --- /dev/null +++ b/test_tools/model_ode_with_functions.txt @@ -0,0 +1,13 @@ + +Species +A,100 + +Mappings +A : +A + +Parameters +volume,1 + +Reactions +reaction_0,{'re': [(1, 'A')], 'pr': [(2, 'A')], 'kin': '(1/(1+exp((A/1000))))'}