def test_pickling(self): # Test pickling a simulation # Test with myokit.Protocol m, p, _ = myokit.load('example') s1 = myokit.LegacySimulation(m, p) s1.pre(123) s_bytes = pickle.dumps(s1) s2 = pickle.loads(s_bytes) self.assertEqual(s1.time(), s2.time()) self.assertEqual(s1.state(), s2.state()) self.assertEqual(s1.default_state(), s2.default_state()) s1.run(123, log=myokit.LOG_NONE) s2.run(123, log=myokit.LOG_NONE) self.assertEqual(s1.time(), s2.time()) self.assertEqual(s1.state(), s2.state()) # Test simulation properties s1.set_tolerance(1e-8, 1e-8) s1.set_min_step_size(1e-2) s1.set_max_step_size(0.1) s2 = pickle.loads(pickle.dumps(s1)) s1.run(23, log=myokit.LOG_NONE) s2.run(23, log=myokit.LOG_NONE) self.assertEqual(s1.time(), s2.time()) self.assertEqual(s1.state(), s2.state()) # Test changed constants s1.set_constant('membrane.C', 1.1) s2 = pickle.loads(pickle.dumps(s1)) s1.run(17, log=myokit.LOG_NONE) s2.run(17, log=myokit.LOG_NONE) self.assertEqual(s1.time(), s2.time()) self.assertEqual(s1.state(), s2.state())
def setUpClass(cls): # Test simulation creation. m, p, x = myokit.load(os.path.join(DIR_DATA, 'lr-1991.mmt')) cls.model = m cls.protocol = p cls.sim = myokit.LegacySimulation(cls.model, cls.protocol)
def test_pacing_values_at_event_transitions(self): # Tests the value of the pacing signal at event transitions # Create a simple model m = myokit.Model() c = m.add_component('c') t = c.add_variable('t') t.set_rhs(0) t.set_binding('time') v = c.add_variable('v') v.set_rhs('0') v.set_binding('pace') x = c.add_variable('x') x.set_rhs(0.1) x.promote(0) # Create step protocol p = myokit.Protocol() p.schedule(0, 0, 2) p.schedule(1, 2, 2) p.schedule(2, 4, 4) p.schedule(3, 8, 2) # Simulate with dynamic logging s = myokit.LegacySimulation(m, p) d = s.run(p.characteristic_time()) time = list(d.time()) value = list(d['c.v']) if False: for i, t in enumerate(d.time()): t = str(np.round(t, 5)) print(t + ' ' * (10 - len(t)) + str(d['c.v'][i])) # Values should be # t 0 1 2 3 4 5 6 7 8 9 10 # p 0 0 1 1 2 2 2 2 3 3 0 self.assertEqual(value[time.index(0.0)], 0) self.assertEqual(value[time.index(0.0) + 1], 0) self.assertEqual(value[time.index(2.0) - 1], 0) self.assertEqual(value[time.index(2.0)], 1) self.assertEqual(value[time.index(2.0) + 1], 1) self.assertEqual(value[time.index(4.0) - 1], 1) self.assertEqual(value[time.index(4.0)], 2) self.assertEqual(value[time.index(4.0) + 1], 2) self.assertEqual(value[time.index(8.0) - 1], 2) self.assertEqual(value[time.index(8.0)], 3) self.assertEqual(value[time.index(8.0) + 1], 3) self.assertEqual(value[time.index(10.0) - 1], 3) self.assertEqual(value[time.index(10.0)], 0) # Simulate with fixed logging s.reset() d = s.run(p.characteristic_time() + 1, log_times=d.time()) time2 = list(d.time()) value2 = list(d['c.v']) self.assertEqual(time, time2) self.assertEqual(value, value2)
def test_interpolation_and_pacing(self): # Test if interpolation results in correct pacing values. # When logging with discontinuous steps, in the adaptive time # CVODE sim, the value of pace must be # 1. The old value *before* the time of the event # 2. The new value *at and after* the time of the event # 3. Back to zero *at and after* the end of the event # (Unless it ends sooner due to a new event arriving) # Load model m = myokit.load_model('example') # Voltage-clamp V (but don't bind it directly) v = m.label('membrane_potential') v.demote() v.set_rhs('-80 + 10 * engine.pace') # Create protocol p = myokit.Protocol() p.schedule(level=1, start=0, duration=5, period=10) # Create simulation s = myokit.LegacySimulation(m, p) # Test if this would result in multiple interpolation steps for logging # i.e. test if the step before each transition was at least 2 log steps # long e = s.run(30).npview() t = e.time() for x in [5, 10, 15, 20, 25]: i = e.find_after(x) if not t[i] - t[i - 1] > 0.2: raise Exception('Issue with test: use longer intervals!') del(e, t, x, i) # Now test if correct interpolated values are returned by periodic # logging. d = s.run(30, log_interval=0.1).npview() # Test bound variable p = d['engine.pace'] self.assertTrue(np.all(p[0:50] == 1)) self.assertTrue(np.all(p[50:100] == 0)) self.assertTrue(np.all(p[100:150] == 1)) self.assertTrue(np.all(p[150:200] == 0)) self.assertTrue(np.all(p[200:250] == 1)) self.assertTrue(np.all(p[250:300] == 0)) # Test variable dependent on bound variable p = d['membrane.V'] self.assertTrue(np.all(p[0:50] == -70)) self.assertTrue(np.all(p[50:100] == -80)) self.assertTrue(np.all(p[100:150] == -70)) self.assertTrue(np.all(p[150:200] == -80)) self.assertTrue(np.all(p[200:250] == -70)) self.assertTrue(np.all(p[250:300] == -80))
def test_simulation_error_2(self): # Test for simulation error detection: failure occurred too often. # Cvode error (test failure occurred too many times) m = self.model.clone() v = m.get('membrane.V') v.set_rhs(myokit.Multiply(v.rhs(), myokit.Number(1e18))) s = myokit.LegacySimulation(m, self.protocol) with WarningCollector(): self.assertRaisesRegex( myokit.SimulationError, 'numerical error', s.run, 5000)
def test_last_evaluations_and_steps(self): # Test :meth:`LegacySimulation.last_number_of_evaluations()` and # :meth:`LegacySimulation.last_number_of_steps()` s = myokit.LegacySimulation(self.model, self.protocol) self.assertEqual(s.last_number_of_evaluations(), 0) self.assertEqual(s.last_number_of_steps(), 0) s.run(1) self.assertTrue(s.last_number_of_evaluations() > 0) self.assertTrue(s.last_number_of_steps() > 0) self.assertNotEqual( s.last_number_of_evaluations(), s.last_number_of_steps())
def test_apd(self): # Test the apd rootfinding routine s = myokit.LegacySimulation( self.model, self.protocol, apd_var='membrane.V') s.set_tolerance(1e-8, 1e-8) d, apds = s.run(1800, log=myokit.LOG_NONE, apd_threshold=-70) # Check with threshold equal to V self.assertEqual(len(apds['start']), 2) self.assertEqual(len(apds['duration']), 2) self.assertAlmostEqual(apds['start'][0], 1.19, places=1) self.assertAlmostEqual(apds['start'][1], 1001.19, places=1) self.assertAlmostEqual(apds['duration'][0], 383.88262, places=0) self.assertAlmostEqual(apds['duration'][1], 378.31448, places=0)
def test_last_state(self): # Returns the last state before an error, or None. m = self.model.clone() istim = m.get('membrane.i_stim') istim.set_rhs('engine.pace / stim_amplitude') s = myokit.LegacySimulation(m, self.protocol) self.assertIsNone(s.last_state()) s.run(1) self.assertIsNone(s.last_state()) s.set_constant('membrane.i_stim.stim_amplitude', 0) s.reset() self.assertRaisesRegex(myokit.SimulationError, "at t = 0", s.run, 5) self.assertEqual(len(s.last_state()), len(s.state())) self.assertEqual(s.last_state(), s.state())
def test_point_list_2(self): # Test how the point-list logging performs when some of the logging # points overlap with protocol change points. # Load model m = myokit.load_model(os.path.join(DIR_DATA, 'lr-1991.mmt')) # Voltage clamp m.binding('pace').set_binding(None) v = m.get('membrane.V') v.demote() v.set_rhs(0) v.set_binding('pace') #TODO: Implement chaining like this? #m.get('membrane.V').demote().set_rhs(0).set_binding('pace') # Create step protocol dt = 0.1 steps = [ [-80, 250.1], [-120, 50], [-80, 200], [40, 1000], [-120, 500], [-80, 1000], [-30, 3500], [-120, 500], [-80, 1000], ] p = myokit.Protocol() for f, t in steps: p.add_step(f, t) # Create set of times that overlap with change points times = np.arange(80000) * dt # Create simulation s = myokit.LegacySimulation(m, p) # Run d = s.run(8000, log_times=times).npview() # Check if logging points show correct pacing value # In an earlier implementation, rounding errors and a difference in the # implementation of passing logpoints and passing protocol points could # cause the log point to be just before the protocol change. # In this case, a change at t=120.0 would only be picked up at t=120.1 # (but not consistently!) # The below code checks for this offset = 0 for v, t in steps[:-1]: offset += t e = d.trim(offset - dt, offset + 2 * dt) self.assertNotEqual(e['membrane.V'][0], e['membrane.V'][1])
def test_apd_tracking(self): # Test the APD calculation method. # More testing is done in test_datalog.py! # Apd var is not a state v = self.model.get('ina.INa') self.assertRaisesRegex( ValueError, 'must be a state', myokit.LegacySimulation, self.model, self.protocol, apd_var=v) # Set a valid apd variable v = self.model.get('ik.x') sim = myokit.LegacySimulation( self.model, self.protocol, apd_var=v) sim.run(1, apd_threshold=12) # No apd var given, but threshold provided self.assertRaisesRegex( ValueError, 'without apd_var', self.sim.run, 1, apd_threshold=12)
def test_sim_stats(self): # Test extraction of simulation statistics m, p, _ = myokit.load('example') rt = m['engine'].add_variable('realtime') rt.set_rhs(0) rt.set_binding('realtime') ev = m['engine'].add_variable('evaluations') ev.set_rhs(0) ev.set_binding('evaluations') s = myokit.LegacySimulation(m, p) d = s.run(100, log=myokit.LOG_BOUND).npview() self.assertIn('engine.realtime', d) self.assertIn('engine.evaluations', d) rt, ev = d['engine.realtime'], d['engine.evaluations'] self.assertEqual(len(d.time()), len(rt)) self.assertEqual(len(d.time()), len(ev)) self.assertTrue(np.all(rt >= 0)) self.assertTrue(np.all(ev >= 0)) self.assertTrue(np.all(rt[1:] >= rt[:-1])) self.assertTrue(np.all(ev[1:] >= ev[:-1]))
def test_progress_reporter(self): # Test running with a progress reporter. # Test if it works sim = myokit.LegacySimulation(self.model, self.protocol) with myokit.tools.capture() as c: sim.run(110, progress=myokit.ProgressPrinter()) c = c.text().splitlines() self.assertEqual(len(c), 2) p = re.compile(re.escape('[0.0 minutes] 1.9 % done, estimated ') + '[0-9]+' + re.escape(' seconds remaining')) self.assertIsNotNone(p.match(c[0])) p = re.compile(re.escape('[0.0 minutes] 100.0 % done, estimated ') + '[0-9]+' + re.escape(' seconds remaining')) self.assertIsNotNone(p.match(c[1])) # Not a progress reporter self.assertRaisesRegex( ValueError, 'ProgressReporter', self.sim.run, 5, progress=12) # Cancel from reporter self.assertRaises( myokit.SimulationCancelledError, self.sim.run, 1, progress=CancellingReporter(0))
def test_cvode_simulation_with_zero_states(self): # Tests running cvode simulations on models with no ODEs # Create a model without states m1 = myokit.Model() c = m1.add_component('c') t = c.add_variable('t') t.set_rhs(0) t.set_binding('time') v = c.add_variable('v') v.set_rhs('0') v.set_binding('pace') w = c.add_variable('w') w.set_rhs('2 * v') # Create a model with a state m2 = m1.clone() z = m2.get('c').add_variable('z') z.set_rhs(0.1) z.promote(0) # Test without protocol and dynamic logging s1 = myokit.LegacySimulation(m1) d1 = s1.run(5) self.assertEqual(len(d1.time()), 2) self.assertEqual(list(d1.time()), [0, 5]) self.assertEqual(list(d1['c.w']), [0, 0]) s2 = myokit.LegacySimulation(m2) d2 = s2.run(6, log_times=d1.time()) self.assertEqual(d1.time(), d2.time()) self.assertEqual(d1['c.w'], d2['c.w']) # Test with a protocol and dynamic logging p = myokit.Protocol() p.schedule(0, 0, 2) p.schedule(1, 2, 2) p.schedule(2, 4, 4) p.schedule(3, 8, 2) s1.reset() s1.set_protocol(p) d1 = s1.run(p.characteristic_time()) self.assertEqual(len(d1.time()), 5) self.assertEqual(list(d1.time()), [0, 2, 4, 8, 10]) self.assertEqual(list(d1['c.w']), [0, 2, 4, 6, 0]) s2.reset() s2.set_protocol(p) d2 = s2.run(p.characteristic_time() + 1, log_times=d1.time()) self.assertEqual(d1.time(), d2.time()) self.assertEqual(d1['c.w'], d2['c.w']) # Test with fixed logging times s1.reset() d1 = s1.run(p.characteristic_time() + 1, log_times=d1['c.t']) self.assertEqual(list(d1.time()), [0, 2, 4, 8, 10]) self.assertEqual(list(d1['c.w']), [0, 2, 4, 6, 0]) s2.reset() d2 = s2.run(p.characteristic_time() + 1, log_times=d1.time()) self.assertEqual(d1.time(), d2.time()) self.assertEqual(d1['c.w'], d2['c.w']) # Test appending to log s1.reset() d1 = s1.run(5) d1 = s1.run(5, log=d1) self.assertEqual(list(d1.time()), [0, 2, 4, 5, 8, 10]) self.assertEqual(list(d1['c.w']), [0, 2, 4, 4, 6, 0]) # Test with a log interval s1.reset() d1 = s1.run(11, log_interval=1) self.assertEqual(list(d1.time()), [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) self.assertEqual(list(d1['c.w']), [0, 0, 2, 2, 4, 4, 4, 4, 6, 6, 0])
def test_point_list(self): # Test logging with a preset list of points. # Load model m, p, x = myokit.load(os.path.join(DIR_DATA, 'lr-1991.mmt')) # Create simulation s = myokit.LegacySimulation(m, p) # Don't allow decreasing values times = [1, 2, 1] self.assertRaisesRegex( ValueError, 'non-decreasing', s.run, 5, log_times=times) # Can't use together with a log interval self.assertRaisesRegex( ValueError, 'simultaneously', s.run, 5, log_interval=1, log_times=[1, 2, 3]) # Get some odd times times = np.linspace(0, 90, 999) # Test! s.reset() d = s.run(100, log_times=times).npview() self.assertTrue(np.all(d.time() == times)) # Reset and run again s.reset() d = s.run(100, log_times=times).npview() self.assertTrue(np.all(d.time() == times)) # Run in parts s.reset() d = s.run(50, log_times=times) self.assertEqual(len(d.time()), np.where(times >= 50)[0][0]) d = s.run(50, log=d, log_times=times).npview() self.assertTrue(np.all(d.time() == times)) # Pre-pacing s.reset() s.pre(50) s.run(100, log_times=times) self.assertTrue(np.all(d.time() == times)) # Partial logging s.reset() s.run(10) d = s.run(10, log_times=times) imin = np.where(times >= 10)[0][0] imax = np.where(times >= 20)[0][0] self.assertEqual(len(d.time()), imax - imin) self.assertTrue(np.all(d.time() == times[imin:imax])) s.run(20) d = s.run(15, log_times=times) imin = np.where(times >= 40)[0][0] imax = np.where(times >= 55)[0][0] self.assertEqual(len(d.time()), imax - imin) self.assertTrue(np.all(d.time() == times[imin:imax])) # Get some regular times times = [0, 1, 2, 3, 4, 5] s.reset() d = s.run(6, log_times=times).npview() self.assertEqual(len(d.time()), len(times)) self.assertTrue(np.all(d.time() == times)) # Repeated points times = [0, 0, 0, 5, 5, 5] s.reset() d = s.run(6, log_times=times).npview() self.assertEqual(len(d.time()), len(times)) self.assertTrue(np.all(d.time() == times)) # End points not included, unless also visited! s.reset() s.run(5) d = s.run(5, log_times=times).npview() self.assertEqual(len(d.time()), 3) self.assertTrue(np.all(d.time() == times[3:])) d = s.run(5, log_times=times).npview() self.assertEqual(len(d.time()), 0) # Empty list is same as none s.reset() d = s.run(1, log_times=[]) self.assertNotEqual(len(d.time()), 0)
def test_periodic(self): # Test periodic logging m, p, x = myokit.load(os.path.join(DIR_DATA, 'lr-1991.mmt')) s = myokit.LegacySimulation(m, p) self.periodic(s)
def test_dynamic(self): # Test dynamic logging. emax = 1e-6 # Used for equality testing if debug: print('= Simulation :: Dynamic logging =') # Load model & protocol m, p, x = myokit.load(os.path.join(DIR_DATA, 'lr-1991.mmt')) # Create simulation s = myokit.LegacySimulation(m, p) # # Test 1: Simple 5 ms simulation, log_interval 0.5 ms # d = s.run(50, log=['engine.time']) t = d['engine.time'] if debug: print(t[:2]) print(t[-2:]) print('- ' * 10) # Test first point not double self.assertGreater(t[1], t[0]) # Test last point not double self.assertGreater(t[-1], t[-2]) # Test first point is 0 self.assertTrue(np.abs(t[0] - 0) < emax) # Test last point is 50 self.assertTrue(np.abs(t[-1] - 50) < emax) # # Test 2: Very short simulation # s.reset() d = s.run(1, log=['engine.time']) t = d['engine.time'] if debug: print(t[:2]) print(t[-2:]) print('- ' * 10) # Test first point not double self.assertGreater(t[1], t[0]) # Test last point not double self.assertGreater(t[-1], t[-2]) # Test first point is 0 self.assertTrue(np.abs(t[0] - 0) < emax) # Test last point is 50 self.assertTrue(np.abs(t[-1] - 1) < emax) # # Test 3: Stop and start a simulation # s.reset() d = s.run(2, log=['engine.time']) t = d['engine.time'] n = len(d['engine.time']) if debug: print(d['engine.time'][:2]) # Test first point not double self.assertGreater(t[1], t[0]) # Test last point not double self.assertGreater(t[-1], t[-2]) # Test first point is 0 self.assertTrue(np.abs(t[0] - 0) < emax) # Test last point is 2 self.assertTrue(np.abs(t[-1] - 2) < emax) d = s.run(13, log=d) t = d['engine.time'] if debug: print(t[n - 2:n + 2]) # Test last point not double self.assertGreater(t[-1], t[-2]) # Test last point is 2+13 self.assertTrue(np.abs(t[-1] - 15) < emax) # Test intermediary points are different self.assertGreater(t[n], t[n - 1]) n = len(d['engine.time']) d = s.run(15, log=d) t = d['engine.time'] if debug: print(t[n - 2:n + 2]) # Test last point not double self.assertGreater(t[-1], t[-2]) # Test last point is 2 + 13 + 15 self.assertTrue(np.abs(t[-1] - 30) < emax) # Test intermediary points are different self.assertGreater(t[n], t[n - 1]) n = len(d['engine.time']) d = s.run(20, log=d) t = d['engine.time'] if debug: print(t[n - 2:n + 2]) print(t[-2:]) print('- ' * 10) # Test last point not double self.assertGreater(t[-1], t[-2]) # Test last point is 2 + 13 + 15 + 20 self.assertTrue(np.abs(t[-1] - 50) < emax) # Test intermediary points are different self.assertGreater(t[n], t[n - 1])