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 _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 # The simulation is run only if (tmin + duration > tmin). This is a # stronger check than (duration == 0), which will return true even for # very short durations (and will cause zero iterations of the # "while (t < tmax)" loop below). istate = list(self._state) if tmin + duration > tmin: # Lists to return state in rstate = list(istate) rbound = [0, 0, 0, 0] # time, pace, realtime, evaluations # Initialize self._sim.sim_init( tmin, tmax, istate, rstate, rbound, self._protocol, self._fixed_form_protocol, log, log_interval, log_times, 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 e: # Some CVODE errors are set to raise an ArithmeticError, # which users may be able to debug. self._error_state = list(rstate) txt = [ 'A numerical error occurred during simulation at' ' t = ' + str(t) + '.', 'Last reached state: ' ] txt.extend([ ' ' + x for x in self._model.format_state(rstate).splitlines() ]) txt.append('Inputs for binding: ') txt.append(' time = ' + myokit.float.str(rbound[0])) txt.append(' pace = ' + myokit.float.str(rbound[1])) txt.append(' realtime = ' + myokit.float.str(rbound[2])) txt.append(' evaluations = ' + myokit.float.str(rbound[3])) txt.append(str(e)) try: self._model.evaluate_derivatives(rstate) 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(rstate) # 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 = rstate # 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 _run(self, duration, log, log_interval, report_nan, progress, msg): # Simulation times if duration < 0: raise Exception('Simulation time can\'t be negative.') tmin = self._time tmax = tmin + duration # Gather global variables in model g = [] for label in self._global: v = self._model.binding(label) if v is not None: g.append(v.qname()) # Parse log argument log = myokit.prepare_log(log, self._model, dims=self._dims, global_vars=g, if_empty=myokit.LOG_STATE + myokit.LOG_BOUND, allowed_classes=myokit.LOG_STATE + myokit.LOG_INTER + myokit.LOG_BOUND, precision=self._precision) # Create list of intermediary variables that need to be logged inter_log = [] vars_checked = set() for var in log.iterkeys(): var = myokit.split_key(var)[1] if var in vars_checked: continue vars_checked.add(var) var = self._model.get(var) if var.is_intermediary() and not var.is_bound(): inter_log.append(var) # Get preferred platform/device combo from configuration file platform, device = myokit.OpenCL.load_selection() # Compile template into string with kernel code kernel_file = os.path.join(myokit.DIR_CFUNC, KERNEL_FILE) args = { 'model': self._model, 'precision': self._precision, 'native_math': self._native_math, 'bound_variables': self._bound_variables, 'inter_log': inter_log, 'diffusion': self._diffusion_enabled, 'fields': self._fields.keys(), 'paced_cells': self._paced_cells, } if myokit.DEBUG: print('-' * 79) print( self._code(kernel_file, args, line_numbers=myokit.DEBUG_LINE_NUMBERS)) import sys sys.exit(1) kernel = self._export(kernel_file, args) # Logging period (0 = disabled) log_interval = 1e-9 if log_interval is None else float(log_interval) if log_interval <= 0: log_interval = 1e-9 # Create field values vector n = len(self._fields) * self._nx * self._ny if n: field_data = self._fields.itervalues() field_data = [np.array(x, copy=False) for x in field_data] field_data = np.vstack(field_data) field_data = list(field_data.reshape(n, order='F')) else: field_data = [] # 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.') # Run simulation arithmetic_error = False if duration > 0: # Initialize state_in = self._state state_out = list(state_in) self._sim.sim_init( platform, device, kernel, self._nx, self._ny, self._diffusion_enabled, self._gx, self._gy, self._connections, tmin, tmax, self._step_size, state_in, state_out, self._protocol, log, log_interval, [x.qname() for x in inter_log], field_data, ) 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: arithmetic_error = True finally: # Clean even after KeyboardInterrupt or other Exception self._sim.sim_clean() # Update state self._state = state_out # Check for NaN if report_nan and (arithmetic_error or log.has_nan()): txt = ['Numerical error found in simulation logs.'] try: # NaN encountered, show how it happened time, icell, var, value, states, bound = self.find_nan(log) txt.append('Encountered numerical error at t=' + str(time) + ' in cell (' + ','.join([str(x) for x in icell]) + ') when ' + var + '=' + str(value) + '.') n_states = len(states) txt.append('Obtained ' + str(n_states) + ' previous state(s).') if n_states > 1: txt.append('State before:') txt.append(self._model.format_state(states[1])) txt.append('State during:') txt.append(self._model.format_state(states[0])) if n_states > 1: txt.append('Evaluating derivatives at state before...') try: derivs = self._model.eval_state_derivatives( states[1], precision=self._precision) txt.append( self._model.format_state_derivs(states[1], derivs)) except myokit.NumericalError as ee: txt.append(ee.message) except myokit.FindNanError as e: txt.append( 'Unable to pinpoint source of NaN, an error occurred:') txt.append(e.message) raise myokit.SimulationError('\n'.join(txt)) # Return log return log
def _run(self, duration, logf, logt, log_interval, report_nan, progress, msg): # Simulation times if duration < 0: raise Exception('Simulation duration can\'t be negative.') tmin = self._time tmax = tmin + duration # Gather global variables in fiber model gf = [] gt = [] for label in self._global: v = self._modelf.binding(label) if v is not None: gf.append(v.qname()) v = self._modelt.binding(label) if v is not None: gt.append(v.qname()) # Parse logf argument logf = myokit.prepare_log(logf, self._modelf, dims=self._ncellsf, global_vars=gf, if_empty=myokit.LOG_STATE + myokit.LOG_BOUND, allowed_classes=myokit.LOG_STATE + myokit.LOG_BOUND + myokit.LOG_INTER, precision=self._precision) # Parse logt argument logt = myokit.prepare_log(logt, self._modelt, dims=self._ncellst, global_vars=gt, if_empty=myokit.LOG_STATE + myokit.LOG_BOUND, allowed_classes=myokit.LOG_STATE + myokit.LOG_BOUND + myokit.LOG_INTER, precision=self._precision) # Create list of intermediary fiber variables that need to be logged inter_logf = [] vars_checked = set() for var in logf.keys(): var = myokit.split_key(var)[1] if var in vars_checked: continue vars_checked.add(var) var = self._modelf.get(var) if var.is_intermediary() and not var.is_bound(): inter_logf.append(var) # Create list of intermediary tissue variables that need to be logged inter_logt = [] vars_checked = set() for var in logt.keys(): var = myokit.split_key(var)[1] if var in vars_checked: continue vars_checked.add(var) var = self._modelt.get(var) if var.is_intermediary() and not var.is_bound(): inter_logt.append(var) # Get preferred platform/device combo from configuration file platform, device = myokit.OpenCL.load_selection_bytes() # Generate kernels kernel_file = os.path.join(myokit.DIR_CFUNC, KERNEL_FILE) args = { 'precision': self._precision, 'native_math': self._native_math, 'diffusion': True, 'fields': [], 'rl_states': {}, } args['model'] = self._modelf args['vmvar'] = self._vmf args['bound_variables'] = self._bound_variablesf args['inter_log'] = inter_logf args['paced_cells'] = self._paced_cells if myokit.DEBUG: print('-' * 79) print( self._code(kernel_file, args, line_numbers=myokit.DEBUG_LINE_NUMBERS)) else: kernelf = self._export(kernel_file, args) args['model'] = self._modelt args['vmvar'] = self._vmt args['bound_variables'] = self._bound_variablest args['inter_log'] = inter_logt args['paced_cells'] = [] if myokit.DEBUG: print('-' * 79) print( self._code(kernel_file, args, line_numbers=myokit.DEBUG_LINE_NUMBERS)) import sys sys.exit(1) else: kernelt = self._export(kernel_file, args) # Logging period (0 = disabled) log_interval = 1e-9 if log_interval is None else float(log_interval) if log_interval <= 0: log_interval = 1e-9 # 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.') # Run simulation if duration > 0: # Initialize state_inf = self._statef state_int = self._statet state_outf = list(state_inf) state_outt = list(state_int) self._sim.sim_init( platform, device, kernelf, kernelt, self._ncellsf[0], self._ncellsf[1], self._ncellst[0], self._ncellst[1], self._vmf.indice(), self._vmt.indice(), self._gf[0], self._gf[1], self._gt[0], self._gt[1], self._gft, self._cfx, self._ctx, self._cty, tmin, tmax, self._step_size, state_inf, state_int, state_outf, state_outt, self._protocol, logf, logt, log_interval, [x.qname().encode('ascii') for x in inter_logf], [x.qname().encode('ascii') for x in inter_logt], ) try: t = tmin 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 t < tmin: # A numerical error has occurred. break if not progress.update(min((t - tmin) * r, 1)): raise myokit.SimulationCancelledError() else: # Loop without feedback while t < tmax: t = self._sim.sim_step() if t < tmin: # A numerical error has occurred. break finally: # Clean even after KeyboardInterrupt or other Exception self._sim.sim_clean() # Update states self._statef = state_outf self._statet = state_outt # Check for NaN's, print error output if report_nan and (logf.has_nan() or logt.has_nan()): txt = ['Numerical error found in simulation logs.'] try: # NaN encountered, show how it happened part, time, icell, var, value, states, bound = self.find_nan( logf, logt) model = self._modelt if part == 'tissue' else self._modelf txt.append('Encountered numerical error in ' + part + ' simulation at t=' + str(time) + ' in cell (' + ','.join([str(x) for x in icell]) + ') when ' + var + '=' + str(value) + '.') n_states = len(states) txt.append('Obtained ' + str(n_states) + ' previous state(s).') if n_states > 1: txt.append('State before:') txt.append(model.format_state(states[1])) txt.append('State during:') txt.append(model.format_state(states[0])) if n_states > 1: txt.append('Evaluating derivatives at state before...') try: derivs = model.eval_state_derivatives( states[1], precision=self._precision) txt.append( model.format_state_derivatives(states[1], derivs)) except myokit.NumericalError as ee: txt.append(str(ee)) except myokit.FindNanError as e: txt.append( 'Unable to pinpoint source of NaN, an error occurred:') txt.append(str(e)) raise myokit.SimulationError('\n'.join(txt)) # Return logs return logf, logt
def _run(self, duration, log, log_interval, log_times, sensitivities, apd_variable, apd_threshold, progress, msg): # Create benchmarker for profiling and realtime logging # Note: When adding profiling messages, write them in past tense so # that we can show time elapsed for an operation **that has just # completed**. if myokit.DEBUG_SP or self._model.binding('realtime') is not None: b = myokit.tools.Benchmarker() if myokit.DEBUG_SP: b.print('PP Entered _run method.') else: b = None # 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 # Logging interval (None or 0 = disabled) log_interval = 0 if log_interval is None else float(log_interval) if log_interval < 0: log_interval = 0 if log_times is not None and log_interval > 0: raise ValueError( 'The arguments `log_times` and `log_interval` cannot be used' ' simultaneously.') # Check user-specified logging times. # (An empty list of log points counts as disabled) # Note: Checking of values inside the list (converts to float, is non # decreasing) happens in the C code. if log_times is not None: if len(log_times) == 0: log_times = None # List of sensitivity matrices if self._sensitivities: if sensitivities is None: sensitivities = [] else: # Must be list (of lists of lists) if not isinstance(sensitivities, list): raise ValueError( 'The argument `sensitivities` must be None or a list.') else: sensitivities = None # APD measuring root_list = None root_indice = 0 root_threshold = 0 if apd_variable is None: if apd_threshold is not None: raise ValueError( 'APD Threshold given but no `apd_variable` specified.') else: # Get apd variable from this model if isinstance(apd_variable, myokit.Variable): apd_variable = apd_variable.qname() apd_variable = self._model.get(apd_variable) if not apd_variable.is_state(): raise ValueError( 'The `apd_variable` must be a state variable.') # Set up root finding root_list = [] root_indice = apd_variable.indice() 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.') if myokit.DEBUG_SP: b.print('PP Checked arguments.') # Parse log argument log = myokit.prepare_log(log, self._model, if_empty=myokit.LOG_ALL) if myokit.DEBUG_SP: b.print('PP Called prepare_log.') # Run simulation # The simulation is run only if (tmin + duration > tmin). This is a # stronger check than (duration == 0), which will return true even for # very short durations (and will cause zero iterations of the # "while (t < tmax)" loop below). if tmin + duration > tmin: # Initial state and sensitivities state = list(self._state) s_state = None if self._sensitivities: s_state = [list(x) for x in self._s_state] # List to store final bound variables in (for debugging) bound = [0, 0, 0, 0] # Initialize if myokit.DEBUG_SP: b.print('PP Ready to call sim_init.') self._sim.sim_init( # 0. Initial time tmin, # 1. Final time tmax, # 2. Initial and final state state, # 3. Initial and final state sensitivities s_state, # 4. Space to store the bound variable values bound, # 5. Literal values list(self._literals.values()), # 6. Parameter values list(self._parameters.values()), # 7. An event-based pacing protocol self._protocol, # 8. A fixed-form protocol self._fixed_form_protocol, # 9. A DataLog log, # 10. The log interval, or 0 log_interval, # 11. A list of predetermind logging times, or None log_times, # 12. A list to store calculated sensitivities in sensitivities, # 13. The state variable indice for root finding (only used if # root_list is a list) root_indice, # 14. The threshold for root crossing (can be 0 too, only used # if root_list is a list). root_threshold, # 15. A list to store calculated root crossing times and # directions in, or None root_list, # 16. A myokit.tools.Benchmarker or None (if not used) b, # 17. Boolean/int: 1 if we are logging realtime int(self._model.binding('realtime') is not None), ) t = tmin # Run 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 e: # Some CVODE(S) errors are set to raise an ArithmeticError, # which users may be able to debug. if myokit.DEBUG_SP: b.print('PP Caught ArithmeticError.') # Store error state self._error_state = state # Create long error message 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.float.str(bound[0])) txt.append(' pace = ' + myokit.float.str(bound[1])) txt.append(' realtime = ' + myokit.float.str(bound[2])) txt.append(' evaluations = ' + myokit.float.str(bound[3])) txt.append(str(e)) # Check if state derivatives can be evaluated in Python, if # not, add the error to the error message. try: self._model.evaluate_derivatives(state) except myokit.NumericalError as en: txt.append(str(en)) # Raise numerical simulation error raise myokit.SimulationError('\n'.join(txt)) except Exception as e: if myokit.DEBUG_SP: b.print('PP Caught exception.') # Store error state self._error_state = state # Cast known CVODE errors as SimulationError if 'Function CVode()' in str(e): raise myokit.SimulationError(str(e)) # Unknown exception: re-raise raise finally: # Clean even after KeyboardInterrupt or other Exception self._sim.sim_clean() # Update internal state # Both lists were newly created, so this is OK. self._state = state self._s_state = s_state # Simulation complete if myokit.DEBUG_SP: b.print('PP Simulation complete.') # Calculate apds if root_list is not None: 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 if myokit.DEBUG_SP: b.print('PP Root-finding data processed.') # Return if myokit.DEBUG_SP: b.print('PP Call to _run() complete. Returning.') if self._sensitivities is not None: if root_list is not None: return log, sensitivities, apds else: return log, sensitivities elif root_list is not None: return log, apds return log