def test_unresolved_references(self): """ Test parsing models with unresolved references. """ m = """ [[model]] [c] t = 0 bind time x = 5 """ myokit.parse_model(m) # Bad variable in same component m = """ [[model]] [c] t = 0 bind time x = 5 y = z """ self.assertRaises(myokit.ParseError, myokit.parse_model, m) # Bad component m = """ [[model]] [c] t = 0 bind time x = 5 y = d.z """ self.assertRaises(myokit.ParseError, myokit.parse_model, m) # Bad variable in other component m = """ [[model]] [c] t = 0 bind time x = 5 y = d.b [d] a = 12 """ self.assertRaises(myokit.ParseError, myokit.parse_model, m)
def test_stimulus_current_1(self): # Test finding the stimulus current based on annotations m = myokit.parse_model(""" [[model]] c.V = -80 [c] time = 0 bind time pace = 0 bind pace dot(V) = 0.1 i_stim = pace * 10 [pA] in [pA] d = 3 [m] """) # Annotated with stimulus_current: return regardless of other factors x = m.get('c.d') x.set_label('stimulus_current') self.assertEqual(guess.stimulus_current(m), x) x.set_label(None) x.set_label('stimulus_currents') self.assertNotEqual(guess.stimulus_current(m), x) # Alternatively, use oxmeta annotation x.meta['oxmeta'] = 'membrane_stimulus_current' self.assertEqual(guess.stimulus_current(m), x) del (x.meta['oxmeta']) self.assertNotEqual(guess.stimulus_current(m), x)
def test_membrane_potential_1(self): # Test finding the membrane potential based on annotations m = myokit.parse_model(""" [[model]] membrane.V = -80 [membrane] time = 0 bind time dot(V) = 1 [mV/ms] in [mV] [c] x = 5 in [A] """) # Annotated with membrane_potential: return regardless of other factors x = m.get('c.x') x.set_label('membrane_potential') self.assertEqual(guess.membrane_potential(m), x) x.set_label(None) x.set_label('membrane_potentials') self.assertNotEqual(guess.membrane_potential(m), x) # Alternatively, use oxmeta annotation x.meta['oxmeta'] = 'membrane_voltage' self.assertEqual(guess.membrane_potential(m), x) del (x.meta['oxmeta']) self.assertNotEqual(guess.membrane_potential(m), x)
def test_remove_nested(self): # Tests the method to remove nested variables m = myokit.parse_model(""" [[model]] membrane.V = -80 [membrane] t = 0 bind time dot(V) = 1 + x + y x = 1 + a + y + c a = b f = 9 b = 10 - f * g g = 2 c = a + d d = 4 * a y = 12 in [mV] label membrane_potential """) m.validate() m2 = m.clone() v = m2.get('membrane.V') v.set_rhs(0) myokit.lib.guess._remove_nested(v) self.assertEqual(v.unit(), myokit.units.mV) self.assertEqual(len(v), 0)
def test_automatic_creation_with_v_independence(self): # Test with the v-independent states model = myokit.parse_model(MODEL) m = hh.HHModel.from_component(model.get('binding')) self.assertEqual(len(m.states()), 3)
def test_export_reused_variable(self): # Tests exporting when an `inf` or other special variable is used twice # Create model re-using tau and inf m = myokit.parse_model(""" [[model]] m.V = -80 c.x = 0.1 c.y = 0.1 [m] time = 0 bind time i_ion = c.I dot(V) = -i_ion [c] inf = 0.5 tau = 3 dot(x) = (inf - x) / tau dot(y) = (inf - y) / tau I = x * y * (m.V - 50) """) # Export, and read back in e = myokit.formats.easyml.EasyMLExporter() with TemporaryDirectory() as d: path = d.path('easy.model') e.model(path, m) with open(path, 'r') as f: x = f.read() self.assertIn('x_inf =', x) self.assertIn('y_inf =', x) self.assertIn('tau_x =', x) self.assertIn('tau_y =', x)
def test_against_cvode_v_independent(self): # Test with v-independent states model = myokit.parse_model(MODEL) model.get('membrane.V').set_rhs(-80) model.get('membrane.V').demote() model.binding('pace').set_binding(None) model.get('membrane.V').set_binding('pace') # Create a protocol vs = [-30, -20, -10] p = myokit.pacing.steptrain(vsteps=vs, vhold=-120, tpre=8, tstep=2, tpost=0) t = p.characteristic_time() # Run an analytical simulation dt = 0.01 m = hh.HHModel.from_component(model.get('binding')) s1 = hh.AnalyticalSimulation(m, p) d1 = s1.run(t, log_interval=dt).npview() s2 = myokit.Simulation(model, p) s2.set_tolerance(1e-8, 1e-8) d2 = s2.run(t, log_interval=dt).npview() # Test protocol output is the same e = np.abs(d1['membrane.V'] - d2['membrane.V']) self.assertEqual(np.max(e), 0) # Test current output is very similar e = np.abs(d1['binding.I'] - d2['binding.I']) self.assertLess(np.max(e), 2e-4)
def test_function_parsing(self): """ Test parsing of functions. """ # Test simple function m = """ [[model]] [c] t = 0 bind time x = asin(sin(1)) """ m = myokit.parse_model(m) self.assertAlmostEqual(m.get('c.x').eval(), 1) # Test function with multiple arguments m = """ [[model]] [c] t = 0 bind time x = log(8, 2) """ m = myokit.parse_model(m) self.assertAlmostEqual(m.get('c.x').eval(), 3) # Unknown function m = """ [[model]] [c] t = 0 bind time x = blog(8, 2) """ self.assertRaisesRegex(myokit.ParseError, 'Unknown function', myokit.parse_model, m) # Wrong number of arguments m = """ [[model]] [c] t = 0 bind time x = sin(8, 2) """ self.assertRaisesRegex(myokit.ParseError, 'Wrong number of arguments', myokit.parse_model, m)
def create_myokit_model(self): if self.pk_model is None: pk_model = myokit.Model() else: pk_model = self.create_myokit_dosed_pk_model() if self.pd_model is None: pd_model = myokit.Model() else: pd_model = self.pd_model.create_myokit_model() have_both_models = (self.pk_model is not None and self.pd_model is not None) have_no_models = (self.pk_model is None and self.pd_model is None) # use pk model as the base and import the pd model pkpd_model = pk_model # default model is one with just time if have_no_models: pkpd_model = myokit.parse_model(''' [[model]] [myokit] time = 0 [s] bind time in [s] ''') # remove time binding if if have_both_models: time_var = pd_model.get('myokit.time') time_var.set_binding(None) pd_components = list(pd_model.components()) pd_names = [c.name().replace('myokit', 'PD') for c in pd_components] if pd_components: pkpd_model.import_component( pd_components, new_name=pd_names, ) # remove imported time var if have_both_models: imported_pd_component = pkpd_model.get('PD') imported_time = imported_pd_component.get('time') imported_pd_component.remove_variable(imported_time) # do mappings for mapping in self.mappings.all(): pd_var = pkpd_model.get( mapping.pd_variable.qname.replace('myokit', 'PD')) pk_var = pkpd_model.get(mapping.pk_variable.qname) unit_conversion_multiplier = myokit.Unit.conversion_factor( pk_var.unit(), pd_var.unit()) pd_var.set_rhs( myokit.Multiply(myokit.Number(unit_conversion_multiplier), myokit.Name(pk_var))) pkpd_model.validate() return pkpd_model
def test_convert_hh_states_to_inf_tau_form(self): # Tests conversion to inf-tau form m1 = myokit.parse_model(MODEL) self.assertTrue(hh.has_inf_tau_form(m1.get('ikr.a'))) self.assertFalse(hh.has_inf_tau_form(m1.get('ikr.r'))) # Rewrite model m2 = hh.convert_hh_states_to_inf_tau_form(m1) self.assertNotEqual(m1.code(), m2.code()) self.assertTrue(hh.has_inf_tau_form(m1.get('ikr.a'))) self.assertFalse(hh.has_inf_tau_form(m1.get('ikr.r'))) self.assertTrue(hh.has_inf_tau_form(m2.get('ikr.a'))) self.assertTrue(hh.has_inf_tau_form(m2.get('ikr.r'))) # Test only r was affected self.assertEqual(m1.get('ikr.a').code(), m2.get('ikr.a').code()) self.assertNotEqual(m1.get('ikr.r').code(), m2.get('ikr.r').code()) # Test form self.assertEqual(m2.get('ikr.r').rhs().code(), '(inf - ikr.r) / tau') a = m2.get('ikr.r.alpha').eval() b = m2.get('ikr.r.beta').eval() inf = m2.get('ikr.r.inf').eval() tau = m2.get('ikr.r.tau').eval() self.assertAlmostEqual(inf, a / (a + b)) self.assertAlmostEqual(tau, 1 / (a + b)) # Test state values are similar self.assertAlmostEqual(m1.get('ikr.r').eval(), m2.get('ikr.r').eval()) # Second rewrite isn't necessary m3 = hh.convert_hh_states_to_inf_tau_form(m2) self.assertEqual(m2.code(), m3.code()) # First argument must be a myokit model self.assertRaisesRegex(ValueError, 'must be a myokit.Model', hh.convert_hh_states_to_inf_tau_form, []) # Membrane potential given explicitly (not as label) m2 = m1.clone() v = m2.get('membrane.V') v.set_label(None) m3 = hh.convert_hh_states_to_inf_tau_form(m2, v) self.assertNotEqual(m2.code(), m3.code()) # Note: the next methods are called with v from m2, not v from m3! But # this should still work as variables are .get() from the model. self.assertTrue(hh.has_inf_tau_form(m3.get('ikr.a'), v)) self.assertTrue(hh.has_inf_tau_form(m3.get('ikr.r'), v)) # Unknown membrane potential self.assertRaisesRegex(ValueError, 'Membrane potential must be given', hh.convert_hh_states_to_inf_tau_form, m2)
def test_item_at_text_position(self): # Test :meth:`Model.item_at_text_position()`. text = [ '[[model]]', # 1 'c.x = 0', # 2 '', # 3 '[e]', # 4 't = 0 bind time', '', '[c]', 'desc: This is a test component', 'dot(x) = (10 - x) / y', 'y = 5 + y1', ' y1 = 3', '' ] model = myokit.parse_model(text) e = model.get('e') t = model.get('e.t') c = model.get('c') x = model.get('c.x') y = model.get('c.y') y1 = model.get('c.y.y1') def check(line, char, var): tv = model.item_at_text_position(line, char) if var is None: self.assertIsNone(tv) else: token, var2 = tv self.assertIsNotNone(var2) self.assertEqual(var.qname(), var2.qname()) # It doesn't work on initial conditions check(1, 0, None) check(1, 1, None) check(1, 2, None) # Find the component e and its variable check(4, 0, None) check(4, 1, e) check(4, 2, None) check(5, 0, t) check(5, 1, None) # Find the component c and its variables check(7, 1, c) check(9, 4, x) check(10, 0, y) check(11, 4, y1)
def test_stimulus_current_info_duration(self): # Tests guessing of stimulus duration m = myokit.parse_model(""" [[model]] [c] t = 0 [s] bind time in [s] i_stim = if((t - s) % p < d, a, 0 [pA]) in [pA] s = 10 [s] in [s] oxmeta: membrane_stimulus_current_offset p = 1000 [s] in [s] oxmeta: membrane_stimulus_current_period d = 0.5 [s] in [s] a = 5 [pA] in [pA] oxmeta: membrane_stimulus_current_amplitude """) i = guess.stimulus_current_info(m) self.assertEqual(i['current'], m.get('c.i_stim')) # Note: Having labels above allows the method to exit early. We can't # test this mini-optimisation, but it does show up in coverage # Guess from name + (correct unit or None) v = m.get('c.d') self.assertIsNone(i['duration'], v) v.rename('durandurations') i = guess.stimulus_current_info(m) self.assertEqual(i['duration'], v) v.set_unit(None) i = guess.stimulus_current_info(m) self.assertEqual(i['duration'], v) v.set_unit('s^2') i = guess.stimulus_current_info(m) self.assertIsNone(i['duration'], v) v.set_unit('1') i = guess.stimulus_current_info(m) self.assertIsNone(i['duration'], v) # Can't already be used v.set_unit(None) i = guess.stimulus_current_info(m) self.assertEqual(i['duration'], v) m.get('c.s').meta['oxmeta'] = 'hello' v.meta['oxmeta'] = 'membrane_stimulus_current_offset' i = guess.stimulus_current_info(m) self.assertNotEqual(i['duration'], v)
def test_parser(self): """ Test basic meta-data parsing """ # Parse simple model without meta data lines = [ '[[model]]', 'a.x = 0', '[a]', 't = 0 bind time', 'dot(x) = 4', ] m = myokit.parse_model('\n'.join(lines)) self.assertTrue(m.is_valid()) # Test simple meta data def model(key, value): x = list(lines) x.insert(1, key + ':' + value) m = myokit.parse_model('\n'.join(x)) self.assertTrue(m.is_valid()) self.assertEqual(m.meta[key], value) def component(key, value): m = myokit.parse_model('\n'.join(lines + [key + ':' + value])) self.assertTrue(m.is_valid()) self.assertEqual(m.get('a').meta[key], value) def variable(key, value): m = myokit.parse_model('\n'.join(lines + [' ' + key + ':' + value])) self.assertTrue(m.is_valid()) self.assertEqual(m.get('a.x').meta[key], value) def test(key, value): model(key, value) component(key, value) variable(key, value) test('vic', 'bob') # Test empty property test('vic', '') # Test namespaced keys test('vic:bob', 'uvavu') test('vic:bob:eranu', 'uvavu') # Test invalid keys self.assertRaises(myokit.ParseError, test, 'vic.bob', 'uvavu')
def test_stimulus_current_3(self): # Test finding the stimulus current if it's a constant # Find variable furthest from time / pace (in A) m = myokit.parse_model(""" [[model]] [t] time = 0 bind time in [s] i_stim = 5 [A] in [A] i_steam = 4 [A] in [A] """) # Correct name and unit i = m.get('t.i_stim') self.assertEqual(guess.stimulus_current(m), i) # Unit wrong or name wrong: don't return i.set_unit('m') self.assertIsNone(guess.stimulus_current(m)) # Other allowed units i.set_unit('pA') self.assertEqual(guess.stimulus_current(m), i) i.set_unit('A/kF') self.assertEqual(guess.stimulus_current(m), i) i.set_unit('A/hm^2') self.assertEqual(guess.stimulus_current(m), i) i.set_unit(None) self.assertEqual(guess.stimulus_current(m), i) i.set_unit('mV') self.assertIsNone(guess.stimulus_current(m)) # Other allowed names i.set_unit('pA') self.assertEqual(guess.stimulus_current(m), i) i.rename('istim') self.assertEqual(guess.stimulus_current(m), i) i.rename('ist') self.assertEqual(guess.stimulus_current(m), i) i.rename('i_st') self.assertEqual(guess.stimulus_current(m), i) i.rename('ii_st') self.assertIsNone(guess.stimulus_current(m))
def test_manual_creation_with_v_independence(self): # Test with the v-independent states # Load model model = myokit.parse_model(MODEL) # Select a number of states and parameters states = ['binding.act', 'binding.rec', 'binding.b'] parameters = ['binding.kon', 'binding.koff'] # Create a HH model m = hh.HHModel(model, states, parameters) # Also select a current current = 'binding.I' m = hh.HHModel(model, states, parameters, current) # Test steady-state calculation doesn't fail m.steady_state(-80)
def test_stimulus_current_info_period_duration_offset(self): # Tests what happens when a single variable matches multiple words # In this model, amplitude should be x, and at most 2 variables out of # duration, period, and offset can be set. m = myokit.parse_model(""" [[model]] [c] t = 0 [s] bind time in [s] i_stim = if((t - x) % perioddurationoffset < x, 1, 0) x = 5 perioddurationoffset = 2 in [s] """) i = guess.stimulus_current_info(m) self.assertEqual(i['current'], m.get('c.i_stim')) self.assertEqual(i['amplitude'], m.get('c.x')) self.assertIsNone(i['amplitude_expression']) empties = [x for x in i.values() if x is None] self.assertEqual(len(empties), 3) # Don't match period x = m.get('c.perioddurationoffset') x.rename('durationoffset') i = guess.stimulus_current_info(m) empties = [k for k, v in i.items() if v is None] self.assertEqual(len(empties), 3) self.assertIn('period', empties) # Don't match duration x.rename('periodnoffset') i = guess.stimulus_current_info(m) empties = [k for k, v in i.items() if v is None] self.assertEqual(len(empties), 3) self.assertIn('duration', empties) # Don't match offset x.rename('durationperiod') i = guess.stimulus_current_info(m) empties = [k for k, v in i.items() if v is None] self.assertEqual(len(empties), 3) self.assertIn('offset', empties)
def test_deep_deps(self): # Tests the (hidden) method to find all dependencies of a particular # variable. m = myokit.parse_model(""" [[model]] c.v = -80 [c] time = 0 bind time dot(v) = -i_ion i_ion = i1 + i2 i1 = 5 * v i2 = x * v x = 13 / time z = 2 + y y = 3 """) # Simple cases y, z = m.get('c.y'), m.get('c.z') d = guess._deep_deps(y) self.assertEqual(len(d), 0) d = guess._deep_deps(z) self.assertEqual(d[y], 1) self.assertEqual(len(d), 1) # Harder cases v, time, i_ion = m.get('c.v'), m.get('c.time'), m.get('c.i_ion') i1, i2, i2x = m.get('c.i1'), m.get('c.i2'), m.get('c.i2.x') d = guess._deep_deps(i2) self.assertEqual(d[i2x], 1) self.assertEqual(d[time], 2) self.assertEqual(len(d), 2) d = guess._deep_deps(v) self.assertEqual(d[i_ion], 1) self.assertEqual(d[i1], 2) self.assertEqual(d[i2], 2) self.assertEqual(d[i2x], 3) self.assertEqual(d[time], 4) self.assertEqual(len(d), 5)
def test_stimulus_current_info_offset(self): # Tests guessing of stimulus offset m = myokit.parse_model(""" [[model]] [c] t = 0 [s] bind time in [s] i_stim = if((t - s) % p < d, a, 0 [pA]) in [pA] s = 10 [s] in [s] p = 1000 [s] in [s] d = 0.5 [s] in [s] a = 5 [pA] in [pA] """) i = guess.stimulus_current_info(m) self.assertEqual(i['current'], m.get('c.i_stim')) # Guess from name + (correct unit or None) v = m.get('c.d') self.assertIsNone(i['offset'], v) v.rename('foffsetti') i = guess.stimulus_current_info(m) self.assertEqual(i['offset'], v) v.set_unit(None) i = guess.stimulus_current_info(m) self.assertEqual(i['offset'], v) v.set_unit('s^2') i = guess.stimulus_current_info(m) self.assertIsNone(i['offset'], v) v.set_unit('1') i = guess.stimulus_current_info(m) self.assertIsNone(i['offset'], v) # Can't already be used v.set_unit(None) i = guess.stimulus_current_info(m) self.assertEqual(i['offset'], v) v.meta['oxmeta'] = 'membrane_stimulus_current_period' i = guess.stimulus_current_info(m) self.assertNotEqual(i['offset'], v)
def test_rush_larsen_conversion(self): # Tests methods for writing RL state updates m1 = myokit.parse_model(MODEL) m2 = hh.convert_hh_states_to_inf_tau_form(m1) # Test RL update method dt = myokit.Name('dt') rl = hh.get_rl_expression(m2.get('ikr.a'), dt) self.assertEqual(rl.code(), 'inf + (ikr.a - inf) * exp(-(str:dt / tau))') rl = hh.get_rl_expression(m2.get('ikr.r'), dt) self.assertEqual(rl.code(), 'inf + (ikr.r - inf) * exp(-(str:dt / tau))') # Test RL update is close to Euler for small dt m2.get('membrane.V').set_rhs(20) ikr = m2.get('ikr') dt = ikr.add_variable('dt') dt.set_rhs(1e-6) # Test for a a = m2.get('ikr.a') rl = hh.get_rl_expression(a, myokit.Name(dt)) a1 = rl.eval() a2 = a.state_value() + dt.eval() * a.rhs().eval() self.assertAlmostEqual(a1, a2) # And for r r = m2.get('ikr.r') rl = hh.get_rl_expression(r, myokit.Name(dt)) r1 = rl.eval() r2 = r.state_value() + dt.eval() * r.rhs().eval() self.assertAlmostEqual(r1, r2) # Dt must be an expression self.assertRaisesRegex(ValueError, 'must be a myokit.Expression', hh.get_rl_expression, a, 'dt') # Returns None if not in inf-tau form self.assertIsNone( hh.get_rl_expression(m1.get('ikr.r'), myokit.Name(dt)))
def test_sensitivites_initial(self): # Test setting initial sensitivity values. m = myokit.parse_model(''' [[model]] e.y = 2.3 [e] t = 0 bind time p = 1 / 100 dot(y) = 2 * p - y ''') m.validate() #TODO: Test results s = myokit.Simulation(m, sensitivities=(['e.y'], ['e.p', 'init(e.y)'])) # Test bad initial matrix self.assertRaisesRegex(ValueError, 'None or a list', s.run, 10, sensitivities='hello')
def test_unit_conversion(self): # Tests exporting a model that requires unit conversion # Export model m = myokit.parse_model(units_model) e = myokit.formats.easyml.EasyMLExporter() with TemporaryDirectory() as d: path = d.path('easy.model') e.model(path, m) with open(path, 'r') as f: observed = f.read().strip().splitlines() # Get expected output expected = units_output.strip().splitlines() # Compare (line by line, for readable output) for ob, ex in zip(observed, expected): self.assertEqual(ob, ex) self.assertEqual(len(observed), len(expected)) # Test warnings are raised if conversion fails m.get('membrane.V').set_rhs('hh.I1 + mm.I2') m.get('membrane').remove_variable(m.get('membrane.C')) with TemporaryDirectory() as d: path = d.path('easy.model') with WarningCollector() as c: e.model(path, m) self.assertIn('Unable to convert hh.I1', c.text()) self.assertIn('Unable to convert mm.I2', c.text()) m.get('engine.time').set_unit(myokit.units.cm) with TemporaryDirectory() as d: path = d.path('easy.model') with WarningCollector() as c: e.model(path, m) self.assertIn('Unable to convert time units [cm]', c.text())
def test_sensitivity_ordering(self): # Tests that sensitivities are returned in the correct order # Define model model = myokit.parse_model(""" [[model]] name: one_compartment_pk_model # Initial values central.drug_amount = 0.1 dose.drug_amount = 0.1 [central] dose_rate = 0 bind pace in [g/s (1.1574074074074077e-08)] dot(drug_amount) = -( size * e.elimination_rate * drug_concentration) + ( dose.absorption_rate * dose.drug_amount) in [kg (1e-06)] drug_concentration = drug_amount / size in [g/m^3] size = 0.1 in [L] [dose] absorption_rate = 0.1 in [S/F (1.1574074074074077e-05)] dot(drug_amount) = (-absorption_rate * drug_amount + central.dose_rate) in [kg (1e-06)] [e] elimination_rate = 0.1 in [S/F (1.1574074074074077e-05)] time = 0 bind time in [s (86400)] """) # Select all states and possible independents var = ['central.drug_amount', 'dose.drug_amount'] par = [ 'central.size', 'init(central.drug_amount)', 'dose.absorption_rate', 'init(dose.drug_amount)', 'e.elimination_rate', ] # Run and store sim = myokit.Simulation(model, sensitivities=(var, par)) _, s1 = sim.run(6, log=myokit.LOG_NONE, log_interval=2) s1 = np.array(s1) # Now use reverse order var.reverse() par.reverse() # Run and store sim = myokit.Simulation(model, sensitivities=(var, par)) _, s2 = sim.run(6, log=myokit.LOG_NONE, log_interval=2) s2 = np.array(s2) # Reverse results, to get back original order s2 = s2[:, ::-1, ::-1] self.assertTrue(np.all(s1 == s2))
def test_remove_derivative_references(self): # Test the remove_derivative_references() method. m0 = myokit.parse_model(""" [[model]] c.x = 0 c.y = 1 [e] t = 0 bind time [c] dot(x) = (1 - x) * alpha alpha = 3 * beta + dot_x beta = exp(-y) + cc cc = 1.23 dot_x = 3 dot(y) = (12 - y) / 7 z = 3 * dot(y) [d] z = 3 * dot(c.x) / dot(c.y) """) m2 = myokit.parse_model(""" [[model]] c.x = 0 c.y = 1 [e] t = 0 bind time [c] dot(x) = dot_x_1 dot_x_1 = (1 - x) * alpha alpha = 3 * beta + dot_x beta = exp(-y) + cc cc = 1.23 dot_x = 3 dot(y) = dot_y dot_y = (12 - y) / 7 z = 3 * dot_y [d] z = 3 * c.dot_x_1 / c.dot_y """) # Remove derivatives from m1 m1 = m0.clone() m1.remove_derivative_references() # Assert model matches expected code self.assertEqual(m1.code(), m2.code()) # Assert models both produce the same derivatives dy1 = m1.eval_state_derivatives() dy2 = m2.eval_state_derivatives() self.assertEqual(dy1, dy2) # Test time unit is None self.assertIsNone(m1.get('c.dot_y').unit()) # Only one unit set? Then unit is still None m1 = m0.clone() m1.get('c.y').set_unit('mV') m1.remove_derivative_references() self.assertIsNone(m1.get('c.dot_y').unit()) m1 = m0.clone() m1.get('e.t').set_unit('ms') m1.remove_derivative_references() self.assertIsNone(m1.get('c.dot_y').unit()) # Both units set? Then unit is division of two m1 = m0.clone() m1.get('e.t').set_unit('ms') m1.get('c.y').set_unit('mV') m1.remove_derivative_references() self.assertEqual(m1.get('c.dot_y').unit(), myokit.parse_unit('mV/ms'))
def test_sensitivities_with_derivatives(self): # Test various inputs to the `sensitivities` argument are handled # correctly, including sensitivites of derivatives and intermediary # variables depending on derivatives. # Note: passing the wrong values into the sensitivities constructor arg # is tested in the CModel tests. m = myokit.parse_model(''' [[model]] e.x = 1.2 e.y = 2.3 [e] t = 0 bind time p = 1 / 100 q = 2 r = 3 dot(x) = p * q dot(y) = 2 * p - y fx = x^2 fy = r + p^2 + dot(y) ''') m.validate() x0 = m.get('e.x').state_value() y0 = m.get('e.y').state_value() p = m.get('e.p').eval() q = m.get('e.q').eval() r = m.get('e.r').eval() # Dependents is a list of y in dy/dx. # Must refer to state, derivative, or intermediary, given as: # - Variable # - Name # - Derivative # - "qname" # - "dot(qname)" dependents = [ 'e.x', # qname 'e.y', # qname 'dot(e.x)', # dot(qname) m.get('e.y').lhs(), # Derivative m.get('e.fx'), # Variable m.get('e.fy').lhs(), # Name ] # Independents is a list of x in dy/dx # Must refer to literals or initial values, given as: # - Variable # - Name # - InitialValue # - "qname" # - "init(qname)" independents = [ 'e.p', # qname m.get('e.q'), # qname m.get('e.r').lhs(), # Name 'init(e.x)', # init(qname) myokit.InitialValue(myokit.Name(m.get('e.y'))), # InitialValue ] # Run, get output s = myokit.Simulation(m, sensitivities=(dependents, independents)) s.set_tolerance(1e-9, 1e-9) d, e = s.run(8) d, e = d.npview(), np.array(e) t = d.time() # Check solution self.assertTrue(np.allclose(d['e.x'], x0 + p * q * t)) self.assertTrue( np.allclose(d['e.y'], 2 * p + (y0 - 2 * p) * np.exp(-t))) self.assertTrue( np.allclose(d['e.fx'], (p * q * t)**2 + 2 * p * q * t * x0 + x0**2)) self.assertTrue( np.allclose(d['e.fy'], r + p**2 + (2 * p - y0) * np.exp(-t))) self.assertTrue(np.allclose(d['dot(e.x)'], p * q)) self.assertTrue(np.allclose(d['dot(e.y)'], (2 * p - y0) * np.exp(-t))) # Sensitivities of x to (p, q, r, x0, y0) self.assertTrue(np.allclose(e[:, 0, 0], q * t)) self.assertTrue(np.allclose(e[:, 0, 1], p * t)) self.assertTrue(np.allclose(e[:, 0, 2], 0)) self.assertTrue(np.allclose(e[:, 0, 3], 1)) self.assertTrue(np.allclose(e[:, 0, 4], 0)) # Sensitivities of y to (p, q, r, x0, y0) self.assertTrue(np.allclose(e[:, 1, 0], 2 - 2 * np.exp(-t))) self.assertTrue(np.allclose(e[:, 1, 1], 0)) self.assertTrue(np.allclose(e[:, 1, 2], 0)) self.assertTrue(np.allclose(e[:, 1, 3], 0)) self.assertTrue(np.allclose(e[:, 1, 4], np.exp(-t))) # Sensitivities of dot(x) to (p, q, r, x0, y0) self.assertTrue(np.allclose(e[:, 2, 0], q)) self.assertTrue(np.allclose(e[:, 2, 1], p)) self.assertTrue(np.allclose(e[:, 2, 2], 0)) self.assertTrue(np.allclose(e[:, 2, 3], 0)) self.assertTrue(np.allclose(e[:, 2, 4], 0)) # Sensitivities of dot(y) to (p, q, r, x0, y0) self.assertTrue(np.allclose(e[:, 3, 0], 2 * np.exp(-t))) self.assertTrue(np.allclose(e[:, 3, 1], 0)) self.assertTrue(np.allclose(e[:, 3, 2], 0)) self.assertTrue(np.allclose(e[:, 3, 3], 0)) self.assertTrue(np.allclose(e[:, 3, 4], -np.exp(-t))) # Sensitivities of fx to (p, q, r, x0, y0) self.assertTrue( np.allclose(e[:, 4, 0], 2 * p * (q * t)**2 + 2 * q * t * x0)) self.assertTrue( np.allclose(e[:, 4, 1], 2 * q * (p * t)**2 + 2 * p * t * x0)) self.assertTrue(np.allclose(e[:, 4, 2], 0)) self.assertTrue(np.allclose(e[:, 4, 3], 2 * p * q * t + 2 * x0)) self.assertTrue(np.allclose(e[:, 4, 4], 0)) # Sensitivities of fy to (p, q, r, x0, y0) self.assertTrue(np.allclose(e[:, 5, 0], 2 * p + 2 * np.exp(-t))) self.assertTrue(np.allclose(e[:, 5, 1], 0)) self.assertTrue(np.allclose(e[:, 5, 2], 1)) self.assertTrue(np.allclose(e[:, 5, 3], 0)) self.assertTrue(np.allclose(e[:, 5, 4], -np.exp(-t)))
def test_check_units(self): # Test the ``model.check_units`` method. model = myokit.Model('m') component = model.add_component('c') t = component.add_variable('time') t.set_binding('time') # Check units before any rhs or units set s = myokit.UNIT_STRICT self.assertRaisesRegex(myokit.IntegrityError, 'No RHS set', model.check_units) self.assertRaisesRegex(myokit.IntegrityError, 'No RHS set', model.check_units, s) # Check units before any rhs set t.set_unit('s') self.assertRaisesRegex(myokit.IntegrityError, 'No RHS set', model.check_units) self.assertRaisesRegex(myokit.IntegrityError, 'No RHS set', model.check_units, s) # Check mini model with rhs and units, no states t.set_rhs('0 [s]') a = component.add_variable('a') a.set_rhs(1) model.check_units() model.check_units(s) # Check mini model with a state # Strict check should fail: a's RHS should be in 1/s a.promote(0) model.check_units() self.assertRaises(myokit.IncompatibleUnitError, model.check_units, s) a.set_rhs('1 [1/s]') model.check_units(s) b = component.add_variable('b') b.set_rhs(2) c = component.add_variable('c') c.set_rhs('2 * b') model.check_units() model.check_units(s) a.set_rhs('1 [N/s]') b.set_rhs('2 [m]') c.set_rhs('a * b') # No variable units set model.check_units() self.assertRaises(myokit.IncompatibleUnitError, model.check_units, s) # Variable unit set for state a.set_unit('N') # So rhs should be N/s b.set_unit('m') model.check_units() self.assertRaises(myokit.IncompatibleUnitError, model.check_units, s) # Bad derived unit c.set_unit('A') self.assertRaises(myokit.IncompatibleUnitError, model.check_units) self.assertRaises(myokit.IncompatibleUnitError, model.check_units, s) c.set_unit(myokit.parse_unit('N*m')) model.check_units() model.check_units(s) # References use variable unit, not RHS unit! model = myokit.Model('m') component = model.add_component('c') x = component.add_variable('x') y = component.add_variable('y') x.set_unit(None) y.set_unit(None) x.set_rhs('5 [mV]') y.set_rhs('3 [A] + x') # x unit is unspecified, not mV! model.check_units() self.assertRaises(myokit.IncompatibleUnitError, model.check_units, s) # Tokens are used in IncompatibleUnitError messages m = myokit.parse_model('\n'.join([ '[[model]]', '[a]', 't = 0 [ms] bind time', ])) try: m.check_units(s) except myokit.IncompatibleUnitError as e: self.assertIn('on line 3', str(e)) token = e.token() self.assertIsNotNone(token) self.assertEqual(token[2], 3) self.assertEqual(token[3], 0) # Test comparison with floating point issues m = myokit.parse_model('\n'.join([ '[[model]]', '[a]', 'x = 1 [cm^3] bind time', ' in [cm^3]', 'y = 2 [day]', ' in [day]', 'z = 3 [day^3]', ' in [day^3]', 'a = (x / y / y / y) * z', ' in [cm^3]', ])) m.check_units(s)
def test_inf_tau_form(self): # Test methods for working with inf-tau form m = myokit.parse_model(MODEL) self.assertIsInstance(m, myokit.Model) v = m.get('membrane.V') a = m.get('ikr.a') r = m.get('ikr.r') # Test with v detected from label self.assertTrue(hh.has_inf_tau_form(a)) inf, tau = hh.get_inf_and_tau(a) self.assertEqual(inf, m.get('ikr.a.inf')) self.assertEqual(tau, m.get('ikr.a.tau')) self.assertFalse(hh.has_inf_tau_form(r)) self.assertIsNone(hh.get_inf_and_tau(r)) # Test with v argument, no label v.set_label(None) self.assertRaisesRegex(ValueError, 'Membrane potential must be given', hh.has_inf_tau_form, a) self.assertTrue(hh.has_inf_tau_form(a, v)) inf, tau = hh.get_inf_and_tau(a, v) self.assertEqual(inf, m.get('ikr.a.inf')) self.assertEqual(tau, m.get('ikr.a.tau')) self.assertFalse(hh.has_inf_tau_form(r, v)) self.assertIsNone(hh.get_inf_and_tau(r, v)) # Test with v as a constant v.demote() v.set_rhs(-80) self.assertTrue(hh.has_inf_tau_form(a, v)) inf, tau = hh.get_inf_and_tau(a, v) self.assertEqual(inf, m.get('ikr.a.inf')) self.assertEqual(tau, m.get('ikr.a.tau')) self.assertFalse(hh.has_inf_tau_form(r, v)) self.assertIsNone(hh.get_inf_and_tau(r, v)) del (r) # a is not a state self.assertTrue(hh.has_inf_tau_form(a, v)) a.demote() self.assertFalse(hh.has_inf_tau_form(a, v)) a.promote(0) self.assertTrue(hh.has_inf_tau_form(a, v)) # Almost correct forms / different ways to fail def bad(e): a.set_rhs(e) self.assertFalse(hh.has_inf_tau_form(a, v)) a.set_rhs('(inf - a) / tau') self.assertTrue(hh.has_inf_tau_form(a, v)) def good(e): a.set_rhs(e) self.assertTrue(hh.has_inf_tau_form(a, v)) bad('(inf - a) / (1 + tau)') bad('(inf + a) / tau') bad('(inf - 1) / tau') bad('(1 - inf) / tau') bad('(inf - r) / tau') # Inf and tau can't be states m.get('ikr.a').move_variable(m.get('ikr.a.inf'), m.get('ikr'), 'ainf') m.get('ikr.a').move_variable(m.get('ikr.a.tau'), m.get('ikr'), 'atau') self.assertTrue(hh.has_inf_tau_form(a, v)) m.get('ikr.ainf').promote(1) self.assertFalse(hh.has_inf_tau_form(a, v)) m.get('ikr.ainf').demote() self.assertTrue(hh.has_inf_tau_form(a, v)) m.get('ikr.atau').promote(1) self.assertFalse(hh.has_inf_tau_form(a, v)) m.get('ikr.atau').demote() self.assertTrue(hh.has_inf_tau_form(a, v)) # Inf and tau can't depend on other states than v c = m.add_component('ccc') x = c.add_variable('vvv') x.set_rhs(1) x.promote(0) ainf = m.get('ikr.ainf').rhs() m.get('ikr.ainf').set_rhs('2 + ccc.vvv * V') self.assertFalse(hh.has_inf_tau_form(a, v)) m.get('ikr.ainf').set_rhs(ainf) self.assertTrue(hh.has_inf_tau_form(a, v)) atau = m.get('ikr.atau').rhs() m.get('ikr.atau').set_rhs('3 * ccc.vvv * V') self.assertFalse(hh.has_inf_tau_form(a, v)) m.get('ikr.atau').set_rhs(atau) self.assertTrue(hh.has_inf_tau_form(a, v))
def variable(key, value): m = myokit.parse_model('\n'.join(lines + [' ' + key + ':' + value])) self.assertTrue(m.is_valid()) self.assertEqual(m.get('a.x').meta[key], value)
def component(key, value): m = myokit.parse_model('\n'.join(lines + [key + ':' + value])) self.assertTrue(m.is_valid()) self.assertEqual(m.get('a').meta[key], value)
def model(key, value): x = list(lines) x.insert(1, key + ':' + value) m = myokit.parse_model('\n'.join(x)) self.assertTrue(m.is_valid()) self.assertEqual(m.meta[key], value)
def test_alpha_beta_form(self): # Test methods for working with alpha-beta form m = myokit.parse_model(MODEL) self.assertIsInstance(m, myokit.Model) v = m.get('membrane.V') a = m.get('ikr.a') r = m.get('ikr.r') # Test without v (detected from label) self.assertTrue(hh.has_alpha_beta_form(r)) alph, beta = hh.get_alpha_and_beta(r) self.assertEqual(alph, m.get('ikr.r.alpha')) self.assertEqual(beta, m.get('ikr.r.beta')) self.assertFalse(hh.has_alpha_beta_form(a)) self.assertIsNone(hh.get_alpha_and_beta(a)) # Test with v as a state, without a label v.set_label(None) self.assertRaisesRegex(ValueError, 'Membrane potential must be given', hh.has_alpha_beta_form, r) self.assertTrue(hh.has_alpha_beta_form(r, v)) alph, beta = hh.get_alpha_and_beta(r, v) self.assertEqual(alph, m.get('ikr.r.alpha')) self.assertEqual(beta, m.get('ikr.r.beta')) self.assertFalse(hh.has_alpha_beta_form(a, v)) self.assertIsNone(hh.get_alpha_and_beta(a, v)) # Test with v as a constant v.demote() v.set_rhs(-80) self.assertTrue(hh.has_alpha_beta_form(r, v)) alph, beta = hh.get_alpha_and_beta(r, v) self.assertEqual(alph, m.get('ikr.r.alpha')) self.assertEqual(beta, m.get('ikr.r.beta')) self.assertFalse(hh.has_alpha_beta_form(a, v)) self.assertIsNone(hh.get_alpha_and_beta(a, v)) # Almost correct forms / different ways to fail def bad(e): r.set_rhs(e) self.assertFalse(hh.has_alpha_beta_form(r, v)) r.set_rhs('alpha * (1 - r) - beta * r') self.assertTrue(hh.has_alpha_beta_form(r, v)) def good(e): r.set_rhs(e) self.assertTrue(hh.has_alpha_beta_form(r, v)) # r is not a state self.assertTrue(hh.has_alpha_beta_form(r, v)) r.demote() self.assertFalse(hh.has_alpha_beta_form(r, v)) r.promote(0) self.assertTrue(hh.has_alpha_beta_form(r, v)) # Not a minus bad('alpha * (1 - r) + beta * r') # Minus, but terms aren't multiplies bad('alpha / (1 - r) - beta * r') bad('alpha * (1 - r) - beta / r') # Terms in multiplications can be switched good('(1 - r) * alpha - beta * r') good('(1 - r) * alpha - r * beta') good('alpha * (1 - r) - beta * r') # But the correct terms are required bad('alpha * (2 - r) - beta * r') bad('alpha^2 * (1 - r) - beta * r') bad('alpha * (1 - r) - beta * r^2') bad('alpha * (1 - r) - beta^2 * r') # Alpha and beta can't be states m2 = m.clone() m2.get('membrane.V').set_label('membrane_potential') m2.get('ikr.r').move_variable(m2.get('ikr.r.alpha'), m2.get('ikr'), 'ralpha') self.assertTrue(hh.has_alpha_beta_form(m2.get('ikr.r'))) m2.get('ikr.ralpha').promote(1) self.assertFalse(hh.has_alpha_beta_form(m2.get('ikr.r'))) m2.get('ikr.ralpha').demote() m2.get('ikr.r').move_variable(m2.get('ikr.r.beta'), m2.get('ikr'), 'rbeta') self.assertTrue(hh.has_alpha_beta_form(m2.get('ikr.r'))) m2.get('ikr.rbeta').promote(1) # Alpha and beta can't depend on other states than v c = m.add_component('ccc') x = c.add_variable('vvv') x.set_rhs(1) x.promote(0) ralph = m.get('ikr.r.alpha').rhs() self.assertTrue(hh.has_alpha_beta_form(r, v)) m.get('ikr.r.alpha').set_rhs('3 * ccc.vvv + V') self.assertFalse(hh.has_alpha_beta_form(r, v)) m.get('ikr.r.alpha').set_rhs(ralph) self.assertTrue(hh.has_alpha_beta_form(r, v)) ralph = m.get('ikr.r.beta').rhs() m.get('ikr.r.beta').set_rhs('2 + ccc.vvv - V') self.assertFalse(hh.has_alpha_beta_form(r, v)) m.get('ikr.r.beta').set_rhs(ralph) self.assertTrue(hh.has_alpha_beta_form(r, v))