def gotran2latex(filename, params): """ Create LaTeX code from a gotran model """ if not params.sympy_contraction: # A hack to avoid sympy contractions # import gotran.codegeneration.avoidsympycontractions pass # Load Gotran model ode = load_ode(filename) # Check for completeness # TODO: Should raise a more descriptive exception? if not ode.is_complete: raise Exception("Incomplete ODE") params.output = ( params.output or Path(filename).with_name(f"{ode.name}.tex").as_posix() ) # Create a gotran -> LaTeX document generator gen = LatexCodeGenerator(ode, params) info("") info(f"Generating LaTeX files for the {ode.name} ode...") with open(gen.output_file, "w") as f: f.write(gen.generate()) info(" done.")
def gotran2dolfin(filename, params): """ Create ufl code from a gotran model """ # Load Gotran model ode = load_ode(filename) # Create a ufl based model code generator code_gen = DOLFINCodeGenerator(params) output = params.output if output: if not output.endswith(".py"): output += ".py" else: output = filename.replace(".ode", "") + ".py" f = open(output, "w") f.write("from __future__ import division\n\n") f.write(code_gen.init_states_code(ode) + "\n\n") f.write(code_gen.init_parameters_code(ode) + "\n\n") f.write( code_gen.function_code( rhs_expressions(ode, params=code_gen.params.code)) + "\n", )
def gotran2matlab(filename, params): """ Create a matlab code from a gotran model """ timer = Timer(f"Generate Matlab code from {filename}") # noqa: F841 # Load Gotran model ode = load_ode(filename) gen = MatlabCodeGenerator(params) info("") info(f"Generating Matlab files for the {ode.name} ode...") # Collect monitored if params.functions.monitored.generate: monitored = [expr.name for expr in ode.intermediates + ode.state_expressions] else: monitored = None for function_name, code in list(gen.code_dict(ode, monitored).items()): open(f"{ode.name}_{function_name}.m", "w").write(code) info(" done.")
def gotran2cuda(filename, params): """ Create a CUDA file from a gotran model """ # Load Gotran model ode = load_ode(filename) # Create a C code generator gen = CUDACodeGenerator(params) output = params.output if output: if not output.endswith(".cu"): output += ".cu" else: output = filename.replace(".ode", "") + ".cu" info("") info(f"Generating CUDA code for the {ode.name} ode...") code = gen.module_code(ode) info(" done.") with open(output, "w") as f: if params.system_headers: f.write("#include <math.h>\n") f.write("#include <string.h>\n") f.write(code) if params.list_timings: list_timings()
def gotranprobe(filename, params): set_log_level(WARNING) ode = load_ode(filename) set_log_level(INFO) if params.flat_view: print() print(f" Components({len(ode.components)}):") print(join_indent_and_split([comp.name for comp in ode.components])) print() print(f" States({len(ode.full_states)}):") print(join_indent_and_split([state.name for state in ode.full_states])) if len(ode.parameters) > 0: print() print(f" Parameters({len(ode.parameters)}):") print(join_indent_and_split([param.name for param in ode.parameters])) print() print(f" Intermediates({len(ode.intermediates)}): ") print( join_indent_and_split( [interm.name for interm in ode.intermediates])) else: for comp in ode.components: if comp.states or comp.parameters or comp.intermediates: print(f"\n {comp.name}:") if comp.full_states: print( " States: ", ", ".join(state.name for state in comp.full_states), ) if comp.parameters: print( " Parameters: ", ", ".join(param.name for param in comp.parameters), ) if comp.intermediates: print( " Intermediates: ", ", ".join(interm.name for interm in comp.intermediates), ) print( " Dependencies: ", ", ".join( set(dep.name for interm in comp.intermediates for dep in ode.expression_dependencies[interm] if not isinstance(dep, Comment) and dep not in comp.full_states + comp.parameters), ), )
def gotran2py(filename, params): """ Create a c header file from a gotran model """ timer = Timer(f"Generate Python code from {filename}") # Load Gotran model ode = load_ode(filename) # Collect monitored if params.functions.monitored.generate: monitored = [ expr.name for expr in ode.intermediates + ode.state_expressions ] else: monitored = None # Create a Python code generator gen = PythonCodeGenerator( params, ns=params.namespace, import_inside_functions=params.import_inside_functions, ) output = params.output if output: if not output.endswith(".py"): output += ".py" else: output = filename.replace(".ode", "") + ".py" info("") info(f"Generating Python code for the {ode.name} ode...") if params.class_code: code = gen.class_code(ode, monitored=monitored) else: code = gen.module_code(ode, monitored=monitored) info(" done.") with open(output, "w") as f: f.write(code) del timer if params.list_timings: list_timings()
def gotranexport(filename, params): if len(params.components) == 1 and params.components[0] == "": error("Expected at least 1 component.") if params.name == "": error("Expected a name of the exported ODE.") if params.name in params.components: error( "Expected the name of the ODE to be different from the components." ) sub_ode = ODE(params.name) sub_ode.import_ode(load_ode(filename), components=params.components) sub_ode.finalize() sub_ode.save()
def gotran2md(filename): filename = Path(filename) ode = load_ode(filename) parameters = make_table( [[f"${format_expr(p.name)}$", f"{p._repr_latex_()}"] for p in ode.parameters], header_names=["Name", "Value"], ) states = make_table( [[f"${format_expr(p.name)}$", f"{p._repr_latex_()}"] for p in ode.states], header_names=["Name", "Value"], ) expr = "\n\n".join( [ f"$$\n{p._repr_latex_name()} = {p._repr_latex_expr()}\n$$" for p in ode.intermediates ], ) state_expr = "\n\n".join( [ f"$$\n{p._repr_latex_name()} = {p._repr_latex_expr()}\n$$" for p in ode.state_expressions ], ) mdname = filename.with_suffix(".md") with open(mdname, "w") as f: f.write( _template.format( name=ode.name, parameters=parameters, states=states, expr=expr, state_expr=state_expr, ), ) print(f"Saved to {mdname}")
def gotran2cpp(filename, params): """ Create a C++ header file from a gotran model """ timer = Timer(f"Generate C++ code from {filename}") # Load Gotran model ode = load_ode(filename) # Create a C code generator gen = CppCodeGenerator(params) output = params.output if output: if not (output.endswith(".cpp") or output.endswith(".h")): output += ".h" else: output = filename.replace(".ode", "") + ".h" info("") info(f"Generating C++ code for the {ode.name} ode...") if params.class_code: code = gen.class_code(ode) else: code = gen.module_code(ode) info(" done.") with open(output, "w") as f: if params.system_headers: f.write("#include <cmath>\n") f.write("#include <cstring>\n") f.write("#include <stdexcept>\n") f.write(code) del timer if params.list_timings: list_timings()
def gotran2julia(filename, params): """ Create a c header file from a gotran model """ timer = Timer(f"Generate Julia code from {filename}") # Load Gotran model ode = load_ode(filename) # Collect monitored if params.functions.monitored.generate: monitored = [ expr.name for expr in ode.intermediates + ode.state_expressions ] else: monitored = None # Create a C code generator gen = JuliaCodeGenerator(params) output = params.output if output: if not output.endswith(".jl"): output += ".jl" else: output = filename.replace(".ode", "") + ".jl" info("") info(f"Generating Julia code for the {ode.name} ode...") code = gen.module_code(ode, monitored=monitored) info(" done.") with open(output, "w") as f: f.write(code) del timer if params.list_timings: list_timings()
def create_module(ode, path="../ode/"): """ Compile a gotran ode and return the module and corresponding forward method. """ generation = parameters.generation.copy() for what_not in [ "componentwise_rhs_evaluation", "forward_backward_subst", "linearized_rhs_evaluation", "lu_factorization", "jacobian" ]: generation.functions[what_not].generate = False solver = "rush_larsen" generation.solvers[solver].generate = True if isinstance(ode, str): ode = load_ode(path + ode) module = compile_module(ode, "c", [], generation) forward = getattr(module, "forward_" + solver) return module, forward
extract_equations = [] change_state_names = [] # params = CellMLParser.default_parameters() # parser = CellMLParser(model, params=params) # open(parser.name+".ode", "w").write(parser.to_gotran()) # ode = load_ode(parser.name+".ode") for f in glob.glob("*.cellml"): # print # print f params = CellMLParser.default_parameters() parser = CellMLParser(f, params=params) open(parser.name + ".ode", "w").write(parser.to_gotran()) try: ode = load_ode(parser.name + ".ode") except Exception as e: print("***Error: Could not load gotran model", parser.name, e) print() list_timings() clear_timings() print() mathmlparser = parser.mathmlparser parsed_components = parser.components cellml = parser.cellml cellml_namespace = cellml.tag.split("}")[0] + "}" # Grab encapsulation grouping encapsulations = {}
def gotranrun(filename, params): # Copy of default parameters generation = parameters.generation.copy() # Set body parameters generation.code.body.representation = params.code.body_repr generation.code.body.use_cse = params.code.use_cse generation.code.body.optimize_exprs = params.code.optimize_exprs # Set what code we are going to generate and not for what_not in [ "componentwise_rhs_evaluation", "forward_backward_subst", "linearized_rhs_evaluation", "lu_factorization", "jacobian", ]: generation.functions[what_not].generate = False # Always generate code for monitored expressions generation.functions.monitored.generate = True # If scipy is used to solve we generate RHS and potentially a jacobian if params.solver == "scipy": generation.functions.rhs.generate = True generation.functions.jacobian.generate = params.code.generate_jacobian else: generation.solvers[params.solver].generate = True # Compile executeable code from gotran ode model_arguments = params.model_arguments if len(model_arguments) == 1 and model_arguments[0] == "": model_arguments = [] if len(model_arguments) % 2 != 0: error("Expected an even number of values for 'model_arguments'") arguments = dict() for arg_name, arg_value in [ (model_arguments[i * 2], model_arguments[i * 2 + 1]) for i in range(int(len(model_arguments) / 2)) ]: arguments[arg_name] = arg_value ode = load_ode(filename, **arguments) # Check for DAE if ode.is_dae: error( "Can only integrate pure ODEs. {0} includes algebraic states " "and is hence a DAE.".format(ode.name), ) # Get monitored and plot states plot_states = params.plot_y # Get x_values x_name = params.plot_x state_names = [state.name for state in ode.full_states] monitored_plot = [ plot_states.pop(plot_states.index(name)) for name in plot_states[:] if name not in state_names ] monitored = [] all_monitored_names = [] for expr in sorted(ode.intermediates + ode.state_expressions): if expr.name not in monitored: monitored.append(expr.name) all_monitored_names.append(expr.name) # Check valid monitored plot for mp in monitored_plot: if mp not in monitored: error(f"{mp} is not a state or intermediate in this ODE") # Check x_name if x_name not in ["time"] + monitored + state_names: error( "Expected plot_x to be either 'time' or one of the plotable " "variables, got {}".format(x_name), ) # Logic if x_name is not 'time' as we then need to add the name to # either plot_states or monitored_plot if x_name != "time": if x_name in state_names: plot_states.append(x_name) else: monitored_plot.append(x_name) module = compile_module(ode, params.code.language, monitored, generation) parameter_values = params.parameters init_conditions = params.init_conditions if len(parameter_values) == 1 and parameter_values[0] == "": parameter_values = [] if len(init_conditions) == 1 and init_conditions[0] == "": init_conditions = [] if len(parameter_values) % 2 != 0: error("Expected an even number of values for 'parameters'") if len(init_conditions) % 2 != 0: error("Expected an even number of values for 'initial_conditions'") user_params = dict() for param_name, param_value in [ (parameter_values[i * 2], parameter_values[i * 2 + 1]) for i in range(int(len(parameter_values) / 2)) ]: user_params[param_name] = float(param_value) user_ic = dict() for state_name, state_value in [ (init_conditions[i * 2], init_conditions[i * 2 + 1]) for i in range(int(len(init_conditions) / 2)) ]: user_ic[state_name] = float(state_value) # Use scipy to integrate model t0 = 0.0 t1 = params.tstop dt = params.dt rhs = module.rhs jac = module.compute_jacobian if params.code.generate_jacobian else None y0 = module.init_state_values(**user_ic) model_params = module.init_parameter_values(**user_params) # Check for steady state solve if params.steady_state.solve and root: result = root( rhs, y0, args=(0.0, model_params), jac=jac, method=params.steady_state.method, tol=params.steady_state.tol, ) if result.success: y0 = result.x print( "Found stead state:", ", ".join( f"{state.name}: {value:e}" for value, state in zip(y0, ode.full_states) ), ) else: warning(result.message) tsteps = np.linspace(t0, t1, int(t1 / dt) + 1) # What solver should we use if params.solver == "scipy": try: from scipy.integrate import odeint except Exception as e: error(f"Problem importing scipy.integrate.odeint. {e}") results = odeint(rhs, y0, tsteps, Dfun=jac, args=(model_params,)) else: # Get generated forward method forward = getattr(module, "forward_" + params.solver) results = [y0] states = y0.copy() # Integrate solution using generated forward method for ind, t in enumerate(tsteps[:-1]): # Step solver forward(states, t, dt, model_params) results.append(states.copy()) # Plot results if not (plot_states or monitored or params.save_results): return # allocate memory for saving results if params.save_results: save_results = np.zeros( (len(results), 1 + len(state_names) + len(all_monitored_names)), ) all_monitor_inds = np.array( [monitored.index(monitor) for monitor in all_monitored_names], dtype=int, ) all_results_header = ", ".join(["time"] + state_names + all_monitored_names) plot_inds = [module.state_indices(state) for state in plot_states] monitor_inds = np.array( [monitored.index(monitor) for monitor in monitored_plot], dtype=int, ) monitored_get_values = np.zeros(len(monitored), dtype=np.float_) # Allocate memory plot_values = np.zeros((len(plot_states) + len(monitored_plot), len(results))) for ind, (time, res) in enumerate(zip(tsteps, results)): if plot_states: plot_values[: len(plot_states), ind] = res[plot_inds] if monitored_plot or params.save_results: module.monitor(res, time, model_params, monitored_get_values) if monitored_plot: plot_values[len(plot_states) :, ind] = monitored_get_values[monitor_inds] if params.save_results: save_results[ind, 0] = time save_results[ind, 1 : len(state_names) + 1] = res save_results[ind, len(state_names) + 1 :] = monitored_get_values[ all_monitor_inds ] # Save data if params.save_results: np.savetxt( f"{params.basename}.csv", save_results, header=all_results_header, delimiter=", ", ) # Plot data if not (plot_states + monitored_plot): return # Fixes for derivatives monitored_plot_updated = [] for monitor in monitored_plot: expr, what = special_expression(monitor, ode) if what == DERIVATIVE_EXPRESSION: var, dep = expr.groups() if var in ode.present_ode_objects and dep in ode.present_ode_objects: monitored_plot_updated.append(f"d{var}/d{dep}") else: monitored_plot_updated.append(monitor) else: monitored_plot_updated.append(monitor) plot_items = plot_states + monitored_plot if x_name != "time": x_values = plot_values[plot_items.index(x_name)] else: x_values = tsteps plt.rcParams["lines.linewidth"] = 2 # line_styles = cycle([c+s for s in ["-", "--", "-.", ":"] # for c in plt.rcParams["axes.color_cycle"]]) line_styles = cycle( [ c + s for s in ["-", "--", "-.", ":"] for c in ["b", "g", "r", "c", "m", "y", "k"] ], ) plotted_items = 0 for what, values in zip(plot_items, plot_values): if what == x_name: continue plotted_items += 1 plt.plot(x_values, values, next(line_styles)) if plotted_items > 1: plt.legend([f"$\\mathrm{{{latex(value)}}}$" for value in plot_items]) elif plot_items: plt.ylabel(f"$\\mathrm{{{latex(plot_items[0])}}}$") plt.xlabel(f"$\\mathrm{{{latex(x_name)}}}$") plt.title(ode.name.replace("_", "\\_")) plt.show()
def import_ode(self, ode, prefix="", components=None, **arguments): """ Import a Model into the present Model Arguments --------- ode : str, gotran.ODE The ode which should be added. If ode is a str an ODE stored in that file will be loaded. If it is an ODE it will be added directly to the present ODE. prefix : str (optional) A prefix which all state, parameters and intermediates are prefixed with. components : list, tuple of str (optional) A list of components which will either be extracted or excluded from the imported model. If not given the whole ODE will be imported. arguments : dict (optional) Optional arguments which can control loading of model """ timer = Timer("Import ode") # noqa: F841 components = components or [] check_arg(ode, (str, Path, ODE), 0, context=ODE.import_ode) check_arg(prefix, str, 1, context=ODE.import_ode) check_arg(components, list, 2, context=ODE.import_ode, itemtypes=str) # If ode is given directly if isinstance(ode, (str, Path)): # If not load external ODE from gotran.model.loadmodel import load_ode ode = load_ode(ode, **arguments) # Postfix prefix with "_" if prefix is not "" if prefix and prefix[-1] != "_": prefix += "_" # If extracting only a certain components if components: ode = ode.extract_components(ode.name, *components) # Subs for expressions subs = {} old_new_map = {ode: self} def add_comp_and_children(added, comp): "Help function to recursively add components to ode" # Add states and parameters for obj in comp.ode_objects: # Check if obj already excists as a ODE parameter old_obj = self.present_ode_objects.get(str(obj)) if old_obj and self.object_component[old_obj] == self: new_name = obj.name else: new_name = prefix + obj.name if isinstance(obj, State): subs[obj.sym] = added.add_state(new_name, obj.param) elif isinstance(obj, Parameter): # If adding an ODE parameter if comp == ode: # And parameter name already excists in the present ODE if str(obj) in self.present_ode_objects: # Skip it and add the registered symbol for # substitution subs[obj.sym] = self.present_ode_objects[str( obj)].sym else: # If not already present just add unprefixed name subs[obj.sym] = added.add_parameter( obj.name, obj.param) else: subs[obj.sym] = added.add_parameter( new_name, obj.param) # Add child components for child in list(comp.children.values()): added_child = added.add_component(child.name) # Get corresponding component in present ODE old_new_map[child] = added_child add_comp_and_children(added_child, child) # Recursively add states and parameters add_comp_and_children(self, ode) # Iterate over all components to add expressions for comp_name in ode.all_expr_components_ordered: comp = ode.all_components[comp_name] comp_comment = f"Expressions for the {comp.name} component" # Get corresponding component in new ODE added = old_new_map[comp] # Iterate over all objects of the component and save only expressions # and comments for obj in comp.ode_objects: # If saving an expression if isinstance(obj, Expression): # The new sympy expression new_expr = obj.expr.xreplace(subs) # If the component is a Markov model if comp.rates: # Do not save State derivatives if isinstance(obj, StateDerivative): continue # Save rate expressions slightly different elif isinstance(obj, RateExpression): states = obj.states added._add_single_rate( subs[states[0].sym], subs[states[1].sym], new_expr, ) continue # If no prefix we just add the expression by using setattr # magic if prefix == "": setattr(added, str(obj), new_expr) subs[obj.sym] = added.ode_objects.get(obj.name).sym elif isinstance(obj, (StateExpression, StateSolution)): state = subs[obj.state.sym] if isinstance(obj, AlgebraicExpression): subs[obj.sym] = added.add_algebraic( state, new_expr) elif isinstance(obj, StateDerivative): subs[obj.sym] = added.add_derivative( state, added.t, new_expr, ) elif isinstance(obj, StateSolution): subs[obj.sym] = added.add_state_solution( state, new_expr) print(repr(obj), obj.sym) else: error("Should not reach here...") # Derivatives are tricky. Here the der expr and dep var # need to be registered in the ODE already. But they can # be registered with and without prefix, so we need to # check that. elif isinstance(obj, Derivatives): # Get der_expr der_expr = self.root.present_ode_objects.get( obj.der_expr.name) if der_expr is None: # If not found try prefixed version der_expr = self.root.present_ode_objects.get( prefix + obj.der_expr.name, ) if der_expr is None: if prefix: error( "Could not find expression: " "({0}){1} while adding " "derivative".format( prefix, obj.der_expr), ) else: error( "Could not find expression: " "{0} while adding derivative".format( obj.der_expr, ), ) dep_var = self.root.present_ode_objects.get( obj.dep_var.name) if isinstance(dep_var, Time): dep_var = self._time elif dep_var is None: # If not found try prefixed version dep_var = self.root.present_ode_objects.get( prefix + obj.dep_var.name, ) if dep_var is None: if prefix: error( "Could not find expression: " "({0}){1} while adding " "derivative".format( prefix, obj.dep_var), ) else: error( "Could not find expression: " "{0} while adding derivative".format( obj.dep_var, ), ) subs[obj.sym] = added.add_derivative( der_expr, dep_var, new_expr, ) elif isinstance(obj, Intermediate): subs[obj.sym] = added.add_intermediate( prefix + obj.name, new_expr, ) else: error("Should not reach here!") # If saving a comment elif isinstance(obj, Comment) and str(obj) != comp_comment: added.add_comment(str(obj))