def save_all_summary_statistics(basename, ta, tr, ai, ri, iv): """ Saves summary statistics to files. Arguments ``ta`` etc. must be tuples (v, stats). """ d = myokit.DataLog() d['v'] = ta[0] d['ta'] = ta[1] d.save_csv(basename + '-ta.csv') d = myokit.DataLog() d['v'] = tr[0] d['tr'] = tr[1] d.save_csv(basename + '-tr.csv') d = myokit.DataLog() d['v'] = ai[0] d['ai'] = ai[1] d.save_csv(basename + '-ai.csv') d = myokit.DataLog() d['v'] = ri[0] d['ri'] = ri[1] d.save_csv(basename + '-ri.csv') d = myokit.DataLog() d['v'] = iv[0] d['iv'] = iv[1] d.save_csv(basename + '-iv.csv')
def test_from_data_log(self): # Test some edge cases of `DataBlock2d.from_log`. # Test valid construction d = myokit.DataLog() d.set_time_key('time') d['time'] = [1, 2, 3] d['x', 0, 0] = [0, 0, 0] d['x', 1, 0] = [1, 1, 2] d['x', 0, 1] = [1, 2, 2] d['x', 1, 1] = [3, 4, 5] d['x', 0, 2] = [6, 6, 6] d['x', 1, 2] = [7, 7, 2] myokit.DataBlock2d.from_log(d) # Deprecated alias with WarningCollector() as wc: myokit.DataBlock2d.from_DataLog(d) self.assertIn('deprecated', wc.text()) # No time key d.set_time_key(None) self.assertRaises(ValueError, myokit.DataBlock2d.from_log, d) # Bad time key d.set_time_key('z') # Multi-dimensional time key d.set_time_key('0.0.x') self.assertRaises(ValueError, myokit.DataBlock2d.from_log, d) # 1-dimensional stuff d.set_time_key('time') myokit.DataBlock2d.from_log(d) d['y', 0] = [10, 10, 10] d['y', 1] = [20, 20, 20] self.assertRaises(ValueError, myokit.DataBlock2d.from_log, d) # Mismatched dimensions d = myokit.DataLog() d.set_time_key('time') d['time'] = [1, 2, 3] d['x', 0, 0] = [0, 0, 0] d['x', 1, 0] = [1, 1, 2] d['x', 0, 1] = [1, 2, 2] d['x', 1, 1] = [3, 4, 5] d['x', 0, 2] = [6, 6, 6] d['x', 1, 2] = [7, 7, 2] d['y', 0, 0] = [0, 0, 0] d['y', 1, 0] = [1, 1, 2] d['y', 0, 1] = [1, 2, 2] d['y', 1, 1] = [3, 4, 5] d['y', 0, 2] = [6, 6, 6] d['y', 1, 2] = [7, 7, 2] myokit.DataBlock2d.from_log(d) del (d['0.2.y']) del (d['1.2.y']) self.assertRaises(ValueError, myokit.DataBlock2d.from_log, d)
def test_cv(self): # Test the CV method. b = os.path.join(DIR_DATA, 'cv1d.zip') b = myokit.DataBlock1d.load(b) self.assertAlmostEqual(b.cv('membrane.V'), 5.95272837350686004e+01) # Invalid border argument # Negative self.assertRaises(ValueError, b.cv, 'membrane.V', border=-1) # Too large self.assertRaises(ValueError, b.cv, 'membrane.V', border=1000) # No upstroke time = np.linspace(0, 1, 100) down = np.zeros(100) - 85 d = myokit.DataLog() d.set_time_key('time') d['time'] = time d['x', 0] = down d['x', 1] = down d['x', 2] = down d['x', 3] = down d['x', 4] = down b = myokit.DataBlock1d.from_log(d) self.assertEqual(b.cv('x'), 0) # No propagation: Single-cell stimulus d = myokit.DataLog() d.set_time_key('time') d['time'] = time d['x', 0] = np.array(down, copy=True) d['x', 1] = np.array(down, copy=True) d['x', 2] = np.array(down, copy=True) d['x', 3] = np.array(down, copy=True) d['x', 4] = np.array(down, copy=True) d['x', 1][10:] = 40 b = myokit.DataBlock1d.from_log(d) self.assertEqual(b.cv('x'), 0) # No propagation: Multi-cell stimulus d = myokit.DataLog() d.set_time_key('time') d['time'] = time d['x', 0] = np.array(down, copy=True) d['x', 1] = np.array(down, copy=True) d['x', 2] = np.array(down, copy=True) d['x', 3] = np.array(down, copy=True) d['x', 4] = np.array(down, copy=True) d['x', 1][10:] = 40 d['x', 2][10:] = 40 d['x', 3][10:] = 40 b = myokit.DataBlock1d.from_log(d) self.assertEqual(b.cv('x'), 0)
def ratio(self): """ Returns the ratios of the peak conductances (p1 / p2) for step 1 and step 2. The returned value is a :class:`myokit.DataLog` with entries corresponding to the given variable names. """ # Times to wait twaits = np.exp( np.linspace(np.log(self._tmin), np.log(self._tmax), self._nt)) # Variables to log log_vars = [x.qname() for x in self._vars] # Run simulations self._vvar.set_rhs(self._vhold) # Make V a constant s = myokit.Simulation(self._model) log = myokit.DataLog() gvars = [x.qname() for x in self._vars] for g in gvars: log[g] = [] log[self._tvar.qname()] = list(twaits) for twait in twaits: s.set_constant(self._vvar, self._vhold) s.run(self._thold, log=myokit.LOG_NONE) s.set_constant(self._vvar, self._vstep) d1 = s.run(self._tstep1, log=log_vars) s.set_constant(self._vvar, self._vhold) s.run(twait, log=myokit.LOG_NONE) s.set_constant(self._vvar, self._vstep) d2 = s.run(self._tstep2, log=log_vars) for g in gvars: ratio = np.max(d1[g]) ratio = np.nan if ratio == 0 else np.max(d2[g]) / ratio log[g].append(ratio) return log
def test_simple(self): # Test basic functionality. # Create test model m = myokit.Model('test') c = m.add_component('c') t = c.add_variable('time') t.set_rhs('0') t.set_binding('time') v = c.add_variable('V') v.set_rhs('0') v.promote(-80.1) x = c.add_variable('x') x.set_rhs('exp(V)') m.validate() # Create simulation log log = myokit.DataLog() log['c.time'] = np.zeros(10) log['c.V'] = np.linspace(-80.0, 50.0, 10) # Number of repeats repeats = 10 # Run x.set_rhs('1 / (7 * exp((V + 12) / 35) + 9 * exp(-(V + 77) / 6))') b = myokit.RhsBenchmarker(m, [x]) t = b.bench_full(log, repeats) t = b.bench_part(log, repeats) # No errors = pass # Get mean and std after outlier removal mean = b.mean(t) mean, std = b.mean_std(t)
def log_for_interval(protocol, a, b): """ Copied from PacingSystem. This is useful for testing! """ # Test the input a, b = float(a), float(b) if b < a: raise ValueError('The argument `b` cannot be smaller than `a`') # Create a simulation log log = myokit.DataLog() log.set_time_key('time') log['time'] = time = [] log['pace'] = pace = [] # Create a pacing system p = AnsicEventBasedPacing(protocol) # Fill in points t = a v = p.advance(t) time.append(t) pace.append(v) while t < b: t = min(p.next_time(), b) v = p.advance(t) time.append(t) pace.append(v) return log
def create_log_for_times(self, times): """ Creates a :class:`myokit.DataLog` containing the entries `time` and `pace` representing the value of the pacing stimulus at each point. The time entries ``times`` must be an non-descreasing series of non-negative points. """ # Create empty log log = myokit.DataLog() log.set_time_key('time') # Times empty? Then return empty log if len(times) == 0: log['time'] = [] log['pace'] = [] return log # Test times times = np.asarray(times) dt = times[1:] - times[:-1] if np.any(dt < 0): raise ValueError('The argument `times` must contain a' ' non-decreasing sequence of time points.') if times[0] < 0: raise ValueError('Times cannot be negative.') # Create a pacing system and calculate the values p = PacingSystem(self) # Return log log['time'] = list(times) log['pace'] = [p.advance(t) for t in times] return log
def test_from_log(self): # Test some edge cases of `DataBlock1d.from_log`. # Test valid construction d = myokit.DataLog() d.set_time_key('time') d['time'] = [1, 2, 3] d['x', 0] = [0, 0, 0] d['x', 1] = [1, 1, 2] myokit.DataBlock1d.from_log(d) # Deprecated alias with WarningCollector() as wc: myokit.DataBlock1d.from_DataLog(d) self.assertIn('deprecated', wc.text()) # No time key d = myokit.DataLog() d['time'] = [1, 2, 3] d['x', 0] = [0, 0, 0] d['x', 1] = [1, 1, 2] self.assertRaises(ValueError, myokit.DataBlock1d.from_log, d) # Multi-dimensional time key d.set_time_key('0.x') self.assertRaises(ValueError, myokit.DataBlock1d.from_log, d) # 2-dimensional stuff d.set_time_key('time') myokit.DataBlock1d.from_log(d) d['y', 0, 0] = [10, 10, 10] self.assertRaises(ValueError, myokit.DataBlock1d.from_log, d) # Mismatched dimensions d = myokit.DataLog() d.set_time_key('time') d['time'] = [1, 2, 3] d['x', 0] = [0, 0, 0] d['x', 1] = [1, 1, 2] d['y', 0] = [2, 0, 0] d['y', 1] = [3, 1, 2] d['y', 2] = [0, 4, 5] self.assertRaises(ValueError, myokit.DataBlock1d.from_log, d)
def myokit_log(self): """ Returns this file's time series data as a :class:`myokit.DataLog`. """ import myokit log = myokit.DataLog() if len(self._data) > 0: log.set_time_key(next(iter(self._data.keys()))) for k, v in self._data.items(): log[k] = v return log
def test_accessors(self): # Test various accessor methods of :class:`AtfFile`. with TemporaryDirectory() as d: # Create data log log = myokit.DataLog() log.set_time_key('time') log['time'] = np.arange(10) log['sint'] = np.sin(log['time']) log['cost'] = np.cos(log['time']) # Write atf file path = d.path('test.atf') axon.save_atf(log, path) # Read atf file atf = myokit.formats.axon.AtfFile(path) # Test filename() self.assertEqual(atf.filename(), path) # Test iter and getitem self.assertEqual(len(list(iter(atf))), 3) self.assertTrue(np.all(atf['time'] == log['time'])) self.assertTrue(np.all(atf['sint'] == log['sint'])) self.assertTrue(np.all(atf['cost'] == log['cost'])) # Test items() items = list(atf.items()) self.assertEqual(items[0][0], 'time') self.assertEqual(items[1][0], 'sint') self.assertEqual(items[2][0], 'cost') self.assertTrue(np.all(items[0][1] == log['time'])) self.assertTrue(np.all(items[1][1] == log['sint'])) self.assertTrue(np.all(items[2][1] == log['cost'])) # Test keys() self.assertEqual(list(atf.keys()), ['time', 'sint', 'cost']) # Test values() values = list(atf.values()) self.assertTrue(np.all(values[0] == log['time'])) self.assertTrue(np.all(values[1] == log['sint'])) self.assertTrue(np.all(values[2] == log['cost'])) # Test len self.assertEqual(len(atf), 3) # Test info self.assertIn('myokit', atf.info()) # Test version self.assertEqual(atf.version(), '1.0')
def log_for_times(self, times): """ Returns a :class:`myokit.DataLog` containing the entries ``time`` and ``pace`` representing the value of the pacing stimulus at each point. The time entries ``times`` must be an non-descreasing series of non-negative points. """ log = myokit.DataLog() log.set_time_key('time') log['time'] = times log['pace'] = self.value_at_times(times) return log
def test_bad_log(self): """ Test error handling when unsuitable log is used. """ # Create test model m = myokit.Model('test') c = m.add_component('c') t = c.add_variable('time') t.set_rhs('0') t.set_binding('time') v = c.add_variable('V') v.set_rhs('0') v.promote(-80.1) w = c.add_variable('W') w.set_rhs(0) w.promote(10) x = c.add_variable('x') x.set_rhs('exp(V) + W') m.validate() # Create benchmarker b = myokit.RhsBenchmarker(m, [x]) # Missing state variable d = myokit.DataLog() d['c.time'] = np.zeros(10) #d['c.V'] = np.linspace(-80.0, 50.0, 10) d['c.W'] = np.linspace(0.0, 10.0, 10) self.assertRaisesRegex( ValueError, 'State variable <c.V> not found', b.bench_full, d, 1) # Different size log entries d = myokit.DataLog() d['c.time'] = np.zeros(1000) d['c.V'] = np.linspace(-80.0, 50.0, 10) d['c.W'] = np.linspace(0.0, 10.0, 11) self.assertRaisesRegex( ValueError, 'same length', b.bench_full, d, 1)
def log(self): """ Returns a myokit DataLog containing the data currently displayed in the graph area. """ d = myokit.DataLog() if self._data: d['engine.time'] = self._data.time() for index in self._frozen.iterkeys(): x, y, variable = index d[variable, x, y] = self._data.trace(variable, x, y) if self._temp_index: x, y, variable = self._temp_index d[variable, x, y] = self._data.trace(variable, x, y) return d
def log_for_interval(self, a, b, for_drawing=False): """ Returns a :class:`myokit.DataLog` containing the entries ``time`` and ``pace``, representing the value of the pacing stimulus at each point on the interval ``[a, b]``. The time points in the log will be ``a`` and ``b``, and any time in between at which the pacing value changes. If ``for_drawing`` is set to ``True`` each time value where the protocol changes will be listed twice, so that a vertical line can be drawn from the old to the new pacing value. Note that the points returned are from ``a`` to ``b`` inclusive (the interval ``[a, b]``), and so if ``b`` coincides with the end of the protocol a point ``(b, 0)`` will be included in the output (protocol steps are defined as half-open, so include their starting point but not their end point). """ # Test the input a, b = float(a), float(b) if b < a: raise ValueError('The argument `b` cannot be smaller than `a`') # Create a simulation log log = myokit.DataLog() log.set_time_key('time') log['time'] = time = [] log['pace'] = pace = [] # Create a pacing system p = PacingSystem(self) # Fill in points t = a v = p.advance(t) time.append(t) pace.append(v) while t < b: t = min(p.next_time(), b) w = p.advance(t) if for_drawing and v != w: time.append(t) pace.append(v) v = w time.append(t) pace.append(v) return log
def myokit_log(self): """ Creates and returns a:class:`myokit.DataLog` containing all the data from this file. Each channel is stored under its own name, with an indice indicating the record it was from. Time is stored under ``time``. """ log = myokit.DataLog() log.set_time_key('time') log['time'] = np.array(self._time) for i, record in enumerate(self._records): for j, data in enumerate(record): name = self._channel_names[j] log[name, i] = np.array(data) return log
def simulate(self, parameters): # Transform parameters back to model space parameters = self.transformation.detransform(parameters) # Run all simulations logs = [0] * 4 for i, s in enumerate(self.simulations): # Reset simulation s.reset() # Update simulation parameters s.set_parameters(parameters) # Run simulation t = self.times[i] try: d = s.run(t[-1] + 0.1, log_times=t).npview() except myokit.SimulationError: return float('inf') # Store in same format as experimental data e = myokit.DataLog() e.set_time_key('time') e['time'] = d['engine.time'] e['current'] = d['ikr.IKr'] logs[i] = e # Calculate summary statistics try: stats = sumstat.all_summary_statistics(self.cell, pr2_log=logs[0], pr3_log=logs[1], pr4_log=logs[2], pr5_log=logs[3]) except Exception: import traceback e = traceback.format_exc() if 'Optimal parameters not found' not in e: print(e) return None return stats[0][1], stats[1][1], stats[2][1], stats[3][1], stats[4][1]
def run(self): """ Returns a :class:`DataLog` containing the tested cycle lengths as ``cl`` and the measured action potential durations as ``apd``. The diastolic intervals are given as ``di``. Each cycle length is repeated ``beats`` number of times, where ``beats`` is the number of beats specified in the constructor. """ # Run if self._data is None: self._run() # Get data cl, apd = self._data d = myokit.DataLog() d['cl'] = list(cl) d['apd'] = list(apd) d['di'] = list(np.array(cl, copy=False) - np.array(apd, copy=False)) return d
def peaks(self, normalize=False): """ Returns a :class:`myokit.DataLog` containing the tested step voltages and the peak values of the logged variable(s). The names used in the simulation log correspond to those used in the model. For example, when doing an experiment on a Sodium channel the simulation log might have entries ``membrane.v`` and ``ina.g`` where ``membrane.v`` contains the used voltage steps while ``ina.g`` contains the peak values measured at those voltages. If ``normalize`` is set to ``True``, the peak data returned will be normalized by dividing all values by the largest (most positive) peak in the list. If no positive, non-zero values are found no normalization will be applied. If any conversion factor was specified the data will be converted before normalization. """ # Run simulation if needed if self._logs is None: self._run() # Create a copy of the voltage steps v = np.array(self._steps, copy=True) # Create a simulation log d = myokit.DataLog() d[self._vvar] = v # Factor factor = self._factor if self._factor is not None else 1 # Find the peaks for var in self._vars: peaks = np.zeros(len(v)) for k, log in enumerate(self._logs): peaks[k] = log[var][np.argmax(np.abs(log[var]))] d[var] = peaks * factor # Normalize (only if log contains positive values) if normalize: for var in self._vars: x = d[var] m = np.max(x) if m > 0: d[var] = x / m return d
def load_base_aps(force=False): """ Loads the baseline APs from the optical mapping data. """ cachefile = os.path.join(methods.RESULTS, 'exp-aps-base.csv') if not force and os.path.isfile(cachefile): print('Using cached baseline optical APs') log = myokit.DataLog.load_csv(cachefile).npview() time = log.time() aps = [log['ap', i] for i in xrange(len(log) - 1)] return time, np.array(aps) print('Collecting baseline optical APs!') files = glob.glob('optical/Group */Well*.csv') aps = [] log = myokit.DataLog() log.set_time_key('time') for i, filename in enumerate(files): data = np.loadtxt(filename, delimiter=',') # Split into time and voltage t = data[:, 0] * 1000 v = data[:, 1] # Normalise filename = os.path.join(AP_FIGURES, 'exp-base-' + str(i + 1) + '.png') t, v = normalise(t, v, filename=filename, correct_time=True) # Select bit that's present in all traces imin = np.where(t >= 0)[0][0] imax = np.where(t >= 575)[0][0] t = t[imin:imax] v = v[imin:imax] # Store aps.append(v) log['ap', i] = v # Store and return log['time'] = t log.save_csv(cachefile) return t, np.array(aps)
def times(self): """ Returns a :class:`myokit.DataLog` containing the time-to-peak for each logged variable at each voltage step. """ # Run simulation if needed if self._logs is None: self._run() # Create a copy of the voltage steps v = np.array(self._steps, copy=True) # Create a simulation log d = myokit.DataLog() d[self._vvar] = v # Find the peaks for var in self._vars: times = np.zeros(len(v)) for k, log in enumerate(self._logs): times[k] = log[self._tvar][np.argmax(np.abs(log[var]))] d[var] = times return d
def create_log_for_interval(self, a, b, for_drawing=False): """ Creates a :class:`myokit.DataLog` containing the entries `time` and `pace` representing the value of the pacing stimulus at each point. The time points in the log will be on the interval ``[a, b]``, such that every time at which the pacing value changes is present in the log. If ``for_drawing`` is set to ``True`` each time value between ``a`` and ``b`` will be listed twice, so that a vertical line can be drawn from the old to the new pacing value. """ # Test the input a, b = float(a), float(b) if b < a: raise ValueError('The argument `b` cannot be smaller than `a`') # Create a simulation log log = myokit.DataLog() log.set_time_key('time') log['time'] = time = [] log['pace'] = pace = [] # Create a pacing system p = PacingSystem(self) # Fill in points t = a v = p.advance(t, max_time=b) time.append(t) pace.append(v) while t < b: t = p.next_time() if for_drawing: if t != b: time.append(t) pace.append(v) v = p.advance(t, max_time=b) time.append(t) pace.append(v) return log
def calculate_base_apds(time, aps, pt=0.1, force=False): """ Calculates APDs for baseline optical experiments. """ cachefile = os.path.join(methods.RESULTS, 'exp-apds-base-' + str(pt) + '.csv') if not force and os.path.isfile(cachefile): print('Using cached baseline optical APDs') log = myokit.DataLog.load_csv(cachefile).npview() return log['apds'] print('Collecting baseline optical APDs!') apds = [] for i, ap in enumerate(aps): filename = os.path.join(APD_FIGURES, 'exp-base-' + str(i) + '.png') apds.append(calculate_apd(time, ap, pt, filename=filename)[0]) apds = np.array(apds) # Store and return log = myokit.DataLog() log['apds'] = apds log.save_csv(cachefile) return apds
def traces(self): """ Returns the logged traces for each variable as an ordered list of tuples ``(v, DataLog)``. If any conversion factor was specified the data will be converted before returning. """ if self._logs is None: self._run() data = [] steps = iter(self._steps) factor = self._factor if factor is None: factor = np.ones(len(self._steps)) for k, log in enumerate(self._logs): v = steps.next() d = myokit.DataLog() for var in self._vars: d[var] = np.array(log[var]) * factor[k] d[self._tvar] = log[self._tvar] data.append((v, d)) return data
def test_block(self): # Test :meth:`PSimulation.block()`. m, p, x = myokit.load(os.path.join(DIR_DATA, 'lr-1991.mmt')) with WarningCollector() as c: s = myokit.PSimulation(m, p, variables=['membrane.V'], parameters=['ina.gNa', 'ica.gCa']) s.set_step_size(0.002) d, dp = s.run(10, log_interval=2) b = s.block(d, dp) self.assertIsInstance(b, myokit.DataBlock2d) self.assertEqual(b.len0d(), len(d) - 1) self.assertTrue(np.all(b.time() == d.time())) # Log without time e = myokit.DataLog(d) del (e[e.time_key()]) self.assertRaisesRegex(ValueError, 'must contain', s.block, e, dp) # Wrong size derivatives array self.assertRaisesRegex(ValueError, 'shape', s.block, d, dp[:, :-1])
def load_outward_peaks(force=False): """ Loads the peak outward current per cell. """ # Return cached version cachefile = os.path.join(methods.RESULTS, 'exp-peaks.csv') if not force and os.path.isfile(cachefile): print('Using cached peak outward current data!') log = myokit.DataLog.load_csv(cachefile).npview() return log['peaks'] print('Gathering peak outward current data!') peaks = [] for i, long_id in enumerate(ORDER): t, v = load(i) peaks.append(np.max(v)) # Store log = myokit.DataLog() log['peaks'] = peaks log.save_csv(cachefile) # Return return peaks
def calculate_tailored_apds(time, aps, pt=0.1, force=False): """ Calculates the tailored simulation aps using the results from :meth:`normalise_tailored_simulations`. """ cachefile = os.path.join(methods.RESULTS, 'sim-base-apds-' + str(pt) + '.csv') if not force and os.path.isfile(cachefile): print('Using cached baseline tailored APDs') log = myokit.DataLog.load_csv(cachefile).npview() return log['apds'] print('Collecting baseline tailored APDs!') apds = [] for i, long_id in enumerate(outward.ORDER): filename = os.path.join(APD_FIGURES, 'sim-base-' + long_id + '.png') apds.append(calculate_apd(time, aps[i], pt, filename=filename)[0]) apds = np.array(apds) # Store and return log = myokit.DataLog() log['apds'] = apds log.save_csv(cachefile) return apds
def run_simple(self): # Create test model m = myokit.Model('test') c = m.add_component('c') t = c.add_variable('time') t.set_rhs('0') t.set_binding('time') v = c.add_variable('V') v.set_rhs('0') v.promote(-80.1) x = c.add_variable('x') x.set_rhs('exp(V)') m.validate() # Create simulation log log = myokit.DataLog() log['c.time'] = np.zeros(1000) log['c.V'] = np.linspace(-80.0, 50.0, 10) # Number of repeats repeats = 10 # Run x.set_rhs('1 / (7 * exp((V + 12) / 35) + 9 * exp(-(V + 77) / 6))') b = myokit.RhsBenchmarker(m, [x]) t = b.bench_full(log, repeats) t = b.bench_part(log, repeats)
def current(self, parameter, voltage, times): """ Generate current of voltage clamp using the given scalings. - Not fitting on this, so voltage resolution is not as important. - Not running this often, so can setup everything here... """ parameter = np.array(parameter) # Update model parameters if self.transform is not None: parameter = self.transform(parameter) model = myokit.load_model(self._model_file) # Simulate with modified model for i, name in enumerate(self._conductance): ''' # normal way of doing it... self.simulation.set_constant(name, parameter[i] * self.original[i]) ''' # try to set conductance for non-literal... model.get(name).set_rhs( parameter[i] * self.original[i] #''' ) # Get current names of output current = [] m_cur = model_current[self._model_file_name] for name in self.parameters: current.append(m_cur[name]) # Set up voltage clamp #for ion_var, ion_conc in model_ion[self._model_file_name]: # self._fix_concentration(model, ion_var, ion_conc) # Detach voltage for voltage clamp(?) model_v = model.get('membrane.V') model_v.demote() tmp_vhold = vhold model_v.set_rhs(tmp_vhold) model.get('engine.pace').set_binding(None) model_v.set_binding('pace') # Create pre-pacing protocol protocol = pacing.constant(tmp_vhold) # Create pre-pacing simulation simulation1 = myokit.Simulation(model, protocol) simulation2 = myokit.Simulation(model) simulation2.set_fixed_form_protocol( times, voltage ) simulation2.set_tolerance(1e-8, 1e-10) simulation2.set_max_step_size(1e-2) # ms # Run simulation1.reset() simulation2.reset() simulation1.set_state(self.original_state) simulation1.pre(100) simulation2.set_state(simulation1.state()) # Log some beats d = simulation2.run(np.max(times)+0.02, log_times = times, log = current, ).npview() # rename output names d_out = myokit.DataLog() for s, c in m_cur.iteritems(): if s in self.parameters: d_out[s[:-2]] = d[c] del(d) return d_out
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=None, log_interval=0.01, log_times=None): """ Runs a simulation for ``duration`` time units. After the simulation: - The simulation time will be increased by ``duration`` time units. - The simulation state will be updated to the last reached state. Arguments: ``duration`` The number of time units to simulate. ``log`` A log from a previous run can be passed in, in which case the results will be appended to this one. ``log_interval`` The time between logged points. ``log_times`` A pre-defined sequence of times to log at. If set, ``log_interval`` will be ignored. Returns a :class:`myokit.DataLog` with the simulation results. """ # Check arguments duration = float(duration) if duration < 0: raise ValueError('Duration must be non-negative.') log_interval = float(log_interval) if log_interval <= 0: raise ValueError('Log interval must be greater than zero.') # Check log_times if log_times is None: log_times = self._time + np.arange(0, duration, log_interval) # Set up logging if log is None: # Create new log log = myokit.DataLog() log.set_time_key(self._time_key) for key in self._log_keys: log[key] = np.zeros(log_times.shape) offset = 0 else: # Check existing log if len(log.keys()) > len(self._log_keys): raise ValueError('Invalid log: contains extra keys.') try: key = self._time_key # Note: error msg below uses `key` offset = len(log[key]) for key in self._log_keys: log[key] = np.concatenate( (log[key], np.zeros(log_times.shape))) except KeyError: raise ValueError('Invalid log: missing entry for <' + str(key) + '>.') # Run simulation if self._protocol is None: # User defined membrane potential self._run(log, log_times, self._time + duration, offset) else: # Voltage clamp tfinal = self._time + duration while self._time < tfinal: # Run simulation tnext = min(tfinal, self._pacing.next_time()) times = log_times[np.logical_and(log_times >= self._time, log_times < tnext)] self._run(log, times, tnext, offset) offset += len(times) # Update pacing self._membrane_potential = self._pacing.advance(tnext) # Return return log