def _run(self, duration, log, log_interval, apd_threshold, progress, msg): # Reset error state self._error_state = None # Simulation times if duration < 0: raise Exception('Simulation time can\'t be negative.') tmin = self._time tmax = tmin + duration # Parse log argument log = myokit.prepare_log(log, self._model, if_empty=myokit.LOG_ALL) # Logging period (0 = disabled) log_interval = 0 if log_interval is None else float(log_interval) if log_interval < 0: log_interval = 0 # Threshold for APD measurement root_list = None root_threshold = 0 if apd_threshold is not None: if self._apd_var is None: raise ValueError('Threshold given but Simulation object was' ' created without apd_var argument.') else: root_list = [] root_threshold = float(apd_threshold) # Get progress indication function (if any) if progress is None: progress = myokit._Simulation_progress if progress: if not isinstance(progress, myokit.ProgressReporter): raise ValueError( 'The argument "progress" must be either a' ' subclass of myokit.ProgressReporter or None.') # Determine benchmarking mode, create time() function if needed if self._model.binding('realtime') is not None: import timeit bench = timeit.default_timer else: bench = None # Run simulation with myokit.SubCapture(): arithmetic_error = None if duration > 0: # Initialize state = [0] * len(self._state) bound = [0, 0, 0, 0] # time, pace, realtime, evaluations self._sim.sim_init( tmin, tmax, list(self._state), state, bound, self._protocol, self._fixed_form_protocol, log, log_interval, root_list, root_threshold, bench, ) t = tmin try: if progress: # Loop with feedback with progress.job(msg): r = 1.0 / duration if duration != 0 else 1 while t < tmax: t = self._sim.sim_step() if not progress.update(min((t - tmin) * r, 1)): raise myokit.SimulationCancelledError() else: # Loop without feedback while t < tmax: t = self._sim.sim_step() except ArithmeticError as ea: self._error_state = list(state) txt = [ 'A numerical error occurred during simulation at' ' t = ' + str(t) + '.', 'Last reached state: ' ] txt.extend([ ' ' + x for x in self._model.format_state(state).splitlines() ]) txt.append('Inputs for binding: ') txt.append(' time = ' + myokit.strfloat(bound[0])) txt.append(' pace = ' + myokit.strfloat(bound[1])) txt.append(' realtime = ' + myokit.strfloat(bound[2])) txt.append(' evaluations = ' + myokit.strfloat(bound[3])) try: self._model.eval_state_derivatives(state) except myokit.NumericalError as en: txt.append(en.message) raise myokit.SimulationError('\n'.join(txt)) except Exception as e: # Store error state self._error_state = list(state) # Check for zero-step error if e.message[0:9] == 'ZERO_STEP': time = float(e.message[10:]) raise myokit.SimulationError('Too many failed steps at' ' t=' + str(time)) # Unhandled exception: re-raise! raise finally: # Clean even after KeyboardInterrupt or other Exception self._sim.sim_clean() # Update internal state self._state = state # Return if root_list is not None: # Calculate apds and return (log, apds) st = [] dr = [] if root_list: roots = iter(root_list) time, direction = roots.next() tlast = time if direction > 0 else None for time, direction in roots: if direction > 0: tlast = time else: st.append(tlast) dr.append(time - tlast) apds = myokit.DataLog() apds['start'] = st apds['duration'] = dr return log, apds else: # Return log return log
def _compile( self, name, tpl, tpl_vars, libs, libd=None, incd=None, flags=None): """ Compiles a source template into a module and returns it. The module's name is specified by ``name``. The template to compile is given by ``tpl``, while any variables required to process the template should be given as the dict ``tpl_vars``. Any C libraries needed for compilation should be given in the sequence type ``libs``. Library dirs and include dirs can be passed in using ``libd`` and ``incd``. Extra compiler arguments can be given in the list ``flags``. """ src_file = self._source_file() d_cache = tempfile.mkdtemp('myokit') try: # Create output directories d_build = os.path.join(d_cache, 'build') d_modul = os.path.join(d_cache, 'module') os.makedirs(d_build) os.makedirs(d_modul) # Export c file src_file = os.path.join(d_cache, src_file) self._export(tpl, tpl_vars, src_file) # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on # unconventional linux sundials installations, but not on windows # as this can lead to a weird error in setuptools runtime = None if platform.system() == 'Windows' else libd # Create extension ext = Extension( name, sources=[src_file], libraries=libs, library_dirs=libd, runtime_library_dirs=runtime, include_dirs=incd, extra_compile_args=flags, ) # Compile, catch output with myokit.SubCapture() as s: try: setup( name=name, description='Temporary module', ext_modules=[ext], script_args=[ 'build', '--build-base=' + d_build, 'install', '--install-lib=' + d_modul, ]) except (Exception, SystemExit) as e: s.disable() t = [ 'Unable to compile.', 'Error message:', ' ' + e.message, 'Error traceback', traceback.format_exc(), 'Compiler output:', ] captured = s.text().strip() t.extend([' ' + x for x in captured.splitlines()]) raise myokit.CompilationError('\n'.join(t)) # Include module (and refresh in case 2nd model is loaded) (f, pathname, description) = imp.find_module(name, [d_modul]) return imp.load_dynamic(name, pathname) finally: try: shutil.rmtree(d_cache) except Exception: pass
def _run(self, duration, log, log_interval, log_times, apd_threshold, progress, msg): # Reset error state self._error_state = None # Simulation times if duration < 0: raise ValueError('Simulation time can\'t be negative.') tmin = self._time tmax = tmin + duration # Parse log argument log = myokit.prepare_log(log, self._model, if_empty=myokit.LOG_ALL) # Logging period (None or 0 = disabled) log_interval = 0 if log_interval is None else float(log_interval) if log_interval < 0: log_interval = 0 # Logging points (None or empty list = disabled) if log_times is not None: log_times = [float(x) for x in log_times] if len(log_times) == 0: log_times = None else: # Allow duplicates, but always non-decreasing! import numpy as np x = np.asarray(log_times) if np.any(x[1:] < x[:-1]): raise ValueError( 'Values in log_times must be non-decreasing.') del (x, np) if log_times is not None and log_interval > 0: raise ValueError( 'The arguments log_times and log_interval cannot be used' ' simultaneously.') # Threshold for APD measurement root_list = None root_threshold = 0 if apd_threshold is not None: if self._apd_var is None: raise ValueError('Threshold given but Simulation object was' ' created without apd_var argument.') else: root_list = [] root_threshold = float(apd_threshold) # Get progress indication function (if any) if progress is None: progress = myokit._Simulation_progress if progress: if not isinstance(progress, myokit.ProgressReporter): raise ValueError( 'The argument "progress" must be either a' ' subclass of myokit.ProgressReporter or None.') # Determine benchmarking mode, create time() function if needed if self._model.binding('realtime') is not None: import timeit bench = timeit.default_timer else: bench = None # Run simulation with myokit.SubCapture() as capture: if duration > 0: # Initialize state = [0] * len(self._state) bound = [0, 0, 0, 0] # time, pace, realtime, evaluations self._sim.sim_init( tmin, tmax, list(self._state), state, bound, self._protocol, self._fixed_form_protocol, log, log_interval, log_times, root_list, root_threshold, bench, ) t = tmin try: if progress: # Allow progress reporters to bypass the subcapture progress._set_output_stream(capture.bypass()) # Loop with feedback with progress.job(msg): r = 1.0 / duration if duration != 0 else 1 while t < tmax: t = self._sim.sim_step() if not progress.update(min((t - tmin) * r, 1)): raise myokit.SimulationCancelledError() else: # Loop without feedback while t < tmax: t = self._sim.sim_step() except ArithmeticError: self._error_state = list(state) txt = [ 'A numerical error occurred during simulation at' ' t = ' + str(t) + '.', 'Last reached state: ' ] txt.extend([ ' ' + x for x in self._model.format_state(state).splitlines() ]) txt.append('Inputs for binding: ') txt.append(' time = ' + myokit.strfloat(bound[0])) txt.append(' pace = ' + myokit.strfloat(bound[1])) txt.append(' realtime = ' + myokit.strfloat(bound[2])) txt.append(' evaluations = ' + myokit.strfloat(bound[3])) try: self._model.eval_state_derivatives(state) except myokit.NumericalError as en: txt.append(str(en)) raise myokit.SimulationError('\n'.join(txt)) except Exception as e: # Store error state self._error_state = list(state) # Check for known CVODE errors if 'Function CVode()' in str(e): raise myokit.SimulationError(str(e)) # Check for zero step error if str(e)[:10] == 'ZERO_STEP ': # pragma: no cover t = float(str(e)[10:]) raise myokit.SimulationError( 'Maximum number of zero-size steps made at t=' + str(t)) # Unknown exception: re-raise! raise finally: # Clean even after KeyboardInterrupt or other Exception self._sim.sim_clean() # Update internal state self._state = state # Return if root_list is not None: # Calculate apds and return (log, apds) st = [] dr = [] if root_list: roots = iter(root_list) time, direction = next(roots) tlast = time if direction > 0 else None for time, direction in roots: if direction > 0: tlast = time else: st.append(tlast) dr.append(time - tlast) apds = myokit.DataLog() apds['start'] = st apds['duration'] = dr return log, apds else: # Return log return log
def _compile(self, name, tpl, tpl_vars, libs, libd=None, incd=None, flags=None): """ Compiles a source template into a module and returns it. The module's name is specified by ``name``. The template to compile is given by ``tpl``, while any variables required to process the template should be given as the dict ``tpl_vars``. Any C libraries needed for compilation should be given in the sequence type ``libs``. Library dirs and include dirs can be passed in using ``libd`` and ``incd``. Extra compiler arguments can be given in the list ``flags``. """ src_file = self._source_file() d_cache = tempfile.mkdtemp('myokit') try: # Create output directories d_build = os.path.join(d_cache, 'build') d_modul = os.path.join(d_cache, 'module') os.makedirs(d_build) os.makedirs(d_modul) # Export c file src_file = os.path.join(d_cache, src_file) self._export(tpl, tpl_vars, src_file) # Inputs must all be strings name = str(name) src_file = str(src_file) flags = None if flags is None else [str(x) for x in flags] libd = None if libd is None else [str(x) for x in libd] incd = None if incd is None else [str(x) for x in incd] libs = None if libs is None else [str(x) for x in libs] # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on # unconventional linux sundials installations, but not on windows # as this can lead to a weird error in setuptools runtime = libd if (platform.system() == 'Windows' and libd is not None): # pragma: no linux cover runtime = None # Instead, add libd to path on windows try: path = os.environ['path'] except KeyError: path = '' to_add = [x for x in libd if x not in path] if to_add: os.environ['path'] = os.pathsep.join([path] + to_add) # Create extension ext = Extension( name, sources=[src_file], libraries=libs, library_dirs=libd, runtime_library_dirs=runtime, include_dirs=incd, extra_compile_args=flags, ) # Compile, catch output with myokit.SubCapture() as s: try: setup(name=name, description='Temporary module', ext_modules=[ext], script_args=[ str('build'), str('--build-base=' + d_build), str('install'), str('--install-lib=' + d_modul), str('--old-and-unmanageable'), ]) except (Exception, SystemExit) as e: s.disable() t = ['Unable to compile.', 'Error message:'] t.append(str(e)) t.append('Error traceback') t.append(traceback.format_exc()) t.append('Compiler output:') captured = s.text().strip() t.extend([' ' + x for x in captured.splitlines()]) raise myokit.CompilationError('\n'.join(t)) finally: egg = name + '.egg-info' if os.path.exists(egg): shutil.rmtree(egg) # Include module (and refresh in case 2nd model is loaded) return load_module(name, d_modul) finally: try: shutil.rmtree(d_cache) except Exception: # pragma: no cover pass
def _compile(self, name, tpl, tpl_vars, libs, libd=None, incd=None, carg=None, larg=None): """ Compiles a source template into a module and returns it. The module's name is specified by ``name``. The template to compile is given by ``tpl``, while any variables required to process the template should be given as the dict ``tpl_vars``. Any C libraries needed for compilation should be given in the sequence type ``libs``. Library dirs and include dirs can be passed in using ``libd`` and ``incd``. Extra compiler arguments can be given in the list ``carg``, and linker args in ``larg``. """ src_file = self._source_file() working_dir = os.getcwd() d_cache = tempfile.mkdtemp('myokit') try: # Create output directories d_build = os.path.join(d_cache, 'build') os.makedirs(d_build) # Export c file src_file = os.path.join(d_cache, src_file) self._export(tpl, tpl_vars, src_file) # Ensure headers can be read from myokit/_sim if incd is None: incd = [] incd.append(myokit.DIR_CFUNC) # Inputs must all be strings name = str(name) src_file = str(src_file) incd = [str(x) for x in incd] libd = None if libd is None else [str(x) for x in libd] libs = None if libs is None else [str(x) for x in libs] carg = None if carg is None else [str(x) for x in carg] larg = None if larg is None else [str(x) for x in larg] # Uncomment to debug C89 issues ''' if carg is None: carg = [] carg.extend([ #'-Wall', #'-Wextra', #'-Werror=strict-prototypes', #'-Werror=old-style-definition', #'-Werror=missing-prototypes', #'-Werror=missing-declarations', '-Werror=declaration-after-statement', ]) #''' # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on # unconventional linux sundials installations, but not on windows # as this can lead to a weird error in setuptools runtime = libd if platform.system() == 'Windows': # pragma: no linux cover if libd is not None: runtime = None # Determine strategy try: os.add_dll_directory use_add_dll_directory = True except AttributeError: use_add_dll_directory = False # Make windows search the libd directories if use_add_dll_directory: # Python 3.8 and up for path in libd: if os.path.isdir(path): os.add_dll_directory(path) else: # Older versions: add libd to path path = os.environ.get('path', '') if path is None: path = '' to_add = [x for x in libd if x not in path] os.environ['path'] = os.pathsep.join([path] + to_add) # Create extension ext = Extension( name, sources=[src_file], libraries=libs, library_dirs=libd, runtime_library_dirs=runtime, include_dirs=incd, extra_compile_args=carg, extra_link_args=larg, ) # Compile in build directory, catch output with myokit.SubCapture() as s: try: os.chdir(d_build) setup(name=name, description='Temporary module', ext_modules=[ext], script_args=[ str('build_ext'), str('--inplace'), ]) except (Exception, SystemExit) as e: # pragma: no cover s.disable() t = ['Unable to compile.', 'Error message:'] t.append(str(e)) t.append('Error traceback') t.append(traceback.format_exc()) t.append('Compiler output:') captured = s.text().strip() t.extend([' ' + x for x in captured.splitlines()]) raise myokit.CompilationError('\n'.join(t)) # Include module (and refresh in case 2nd model is loaded) return load_module(name, d_build) finally: # Revert changes to working directory os.chdir(working_dir) # Delete cached module try: myokit._rmtree(d_cache) except Exception: # pragma: no cover pass