def test_units(self): # Test writing of units u1 = myokit.parse_unit('kg*m^2/mole^3 (0.123)') m1 = cellml.Model('mmm') m1.add_units('flibbit', u1) m1.add_units('special_volt', myokit.units.Volt) d = m1.add_component('ddd') q = d.add_variable('q', 'flibbit') q.set_equation(myokit.Equation(myokit.Name(q), myokit.Number(2, u1))) xml = cellml.write_string(m1) m2 = cellml.parse_string(xml) q = m2['ddd']['q'] self.assertEqual(q.units().name(), 'flibbit') self.assertEqual(q.units().myokit_unit(), u1) self.assertEqual( m2.find_units('special_volt').myokit_unit(), myokit.units.volt) # Dimensionless units with a multiplier u1 = myokit.parse_unit('1 (0.123)') m1 = cellml.Model('mmm') m1.add_units('flibbit', u1) xml = cellml.write_string(m1) m2 = cellml.parse_string(xml) u2 = m2.find_units('flibbit') u2 = u2.myokit_unit() self.assertEqual(u1, u2)
def test_units(self): # Test writing of units u1 = myokit.parse_unit('kg*m^2/mole^3 (0.123)') u2 = myokit.parse_unit('1 (4.56)') m1 = cellml.Model('mmm') m1.add_units('flibbit', u1) m1.add_units('special_volt', myokit.units.Volt) c = m1.add_component('ccc') c.add_units('flibbit', u2) p = c.add_variable('p', 'flibbit') p.set_rhs(myokit.Number(1, u2)) d = m1.add_component('ddd') q = d.add_variable('q', 'flibbit') q.set_rhs(myokit.Number(2, u1)) xml = cellml.write_string(m1) m2 = cellml.parse_string(xml) p, q = m2['ccc']['p'], m2['ddd']['q'] self.assertEqual(p.units().name(), 'flibbit') self.assertEqual(q.units().name(), 'flibbit') self.assertEqual(p.units().myokit_unit(), u2) self.assertEqual(q.units().myokit_unit(), u1) self.assertEqual( m2.find_units('special_volt').myokit_unit(), myokit.units.volt)
def test_parse_variable(self): """ Tests parse_variable(), uses parse_expression() """ from myokit._parser import parse_variable from myokit._parser import Tokenizer m = myokit.Model('test_model') c = m.add_component('test_component') def p(s, name=None): parse_variable(Tokenizer(s), None, c, convert_proto_rhs=True) if name: return c.var(name) s = """ x1 = 5 """ v = p(s, 'x1') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), None) self.assertEqual(v.rhs().unit(), None) s = """ x2 = 5 [mV] """ v = p(s, 'x2') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), None) self.assertEqual(v.rhs().unit(), myokit.parse_unit('mV')) s = """ x3 = 5 in [mV] """ v = p(s, 'x3') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), myokit.parse_unit('mV')) self.assertEqual(v.rhs().unit(), None) s = """ x4 = 5 [V] : This is x4 """ v = p(s, 'x4') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), None) self.assertEqual(v.rhs().unit(), myokit.units.V) self.assertEqual(v.meta['desc'], 'This is x4')
def __init__(self, value, unit=None): if isinstance(value, myokit.Expression): # Convert myokit.Expression if unit is not None: raise ValueError('Cannot specify a unit when creating a' ' myokit.Quantity from a myokit.Number.') self._value = value.eval() unit = value.unit() self._unit = \ unit if unit is not None else myokit.units.dimensionless else: # Convert other types self._unit = None try: # Convert value to float self._value = float(value) except (ValueError, TypeError): # Try parsing string try: self._value = str(value) parts = value.split('[', 1) except Exception: raise ValueError( 'Value of type ' + str(type(value)) + ' could not be converted to myokit.Quantity.') # Very simple number-with-unit parsing try: self._value = float(parts[0]) except ValueError: raise ValueError('Failed to parse string "' + str(value) + '" as myokit.Quantity.') self._unit = myokit.parse_unit(parts[1].strip()[:-1]) # No unit set yet? Then check unit argument if self._unit is None: if unit is None: self._unit = myokit.units.dimensionless elif isinstance(unit, myokit.Unit): self._unit = unit else: self._unit = myokit.parse_unit(unit) elif unit is not None: raise ValueError('Two units specified for myokit.Quantity.') # Create string representation self._str = str(self._value) + ' ' + str(self._unit)
def test_unit(self): # Test the unit() method that returns a unit conversion factor. m = myokit.Model() c = m.add_component('c') x = c.add_variable('x') x.set_rhs(0) x.set_unit(myokit.parse_unit('mV')) self.assertRaises( myokit.IncompatibleModelError, multi.unit, x, myokit.parse_unit('kg')) self.assertEqual(multi.unit(x, myokit.parse_unit('V')), 0.001) self.assertEqual(multi.unit(x, myokit.parse_unit('mV')), 1) self.assertEqual(multi.unit(x, myokit.parse_unit('uV')), 1000)
def test_versions(self): # Tests writing different CellML versions # CellML 1.0 units = { myokit.parse_unit('pF'): 'picofarad', } w = cellml.CellMLExpressionWriter('1.0') w.set_unit_function(lambda x: units[x]) xml = w.ex(myokit.Number(1, myokit.units.pF)) self.assertIn(cellml.NS_CELLML_1_0, xml) # CellML 1.1 w = cellml.CellMLExpressionWriter('1.1') w.set_unit_function(lambda x: units[x]) xml = w.ex(myokit.Number(1, myokit.units.pF)) self.assertIn(cellml.NS_CELLML_1_1, xml) # CellML 1.2 self.assertRaisesRegex(ValueError, 'Unknown CellML version', cellml.CellMLExpressionWriter, '1.2') # CellML 2.0 w = cellml.CellMLExpressionWriter('2.0') w.set_unit_function(lambda x: units[x]) xml = w.ex(myokit.Number(1, myokit.units.pF)) self.assertIn(cellml.NS_CELLML_2_0, xml)
def test_str(self): # Test :meth:`Unit.str()` # Unit with representation in alternative base km_per_s = myokit.Unit([0, 1, -1, 0, 0, 0, 0], 3) # Myokit doesn't know km/s, it does know m/s so this should become: self.assertEqual(str(km_per_s), '[m/s (1000)]') # Myokit doesn't know MA/m^2 mam2 = myokit.parse_unit('MA/m^2') self.assertEqual(str(mam2), '[A/m^2 (1e+06)]') # Simple units m = myokit.parse_unit('m') self.assertEqual(str(m), '[m]') im = 1 / m self.assertEqual(str(im), '[1/m]') # Predefined complex unit self.assertEqual(str(myokit.units.N), '[N]') # Low mulipliers um = m * 1e-6 self.assertEqual(str(um), '[um]') um3 = myokit.parse_unit('um^3') self.assertEqual(str(um3), '[um^3]') attomol = myokit.parse_unit('amol') self.assertEqual(str(attomol), '[mol (1e-18)]') # High multipliers nn = myokit.units.N * 1e-2 self.assertEqual(str(nn), '[N (0.01)]') nn = myokit.units.N * 1e2 self.assertEqual(str(nn), '[N (100)]') nn = myokit.units.N * 1e7 self.assertEqual(str(nn), '[N (1e+07)]') # Unit very similar to a known unit c = myokit.units.V d = c * 1.000000001 self.assertFalse(c == d) self.assertTrue(myokit.Unit.close(c, d)) self.assertEqual(str(c), '[V]') self.assertEqual(str(d), '[V]')
def test_parse_expression(self): """ Test parse_expression() """ from myokit import parse_expression as p from myokit import Number e = p('5') self.assertIsInstance(e, Number) self.assertEqual(e.eval(), 5.0) self.assertEqual(float(e), 5.0) e = p('5[m]') self.assertIsInstance(e, Number) self.assertEqual(e.eval(), 5.0) self.assertEqual(float(e), 5.0) self.assertEqual(e.unit(), myokit.units.m) e = p('5 [m/s]') self.assertIsInstance(e, Number) self.assertEqual(e.eval(), 5.0) self.assertEqual(float(e), 5.0) self.assertEqual(e.unit(), myokit.parse_unit('m/s')) self.assertEqual(e.unit(), myokit.units.m / myokit.units.s) e = p('+5') self.assertIsInstance(e, myokit.PrefixPlus) self.assertEqual(e.eval(), 5.0) e = p('++5') self.assertIsInstance(e, myokit.PrefixPlus) self.assertEqual(e.eval(), 5.0) e = p('-5') self.assertIsInstance(e, myokit.PrefixMinus) self.assertEqual(e.eval(), -5.0) e = p('--5') self.assertIsInstance(e, myokit.PrefixMinus) self.assertEqual(e.eval(), 5.0) e = p('5 + 2') self.assertIsInstance(e, myokit.Plus) self.assertEqual(e.eval(), 7) e = p('5 + 1 + 1') self.assertIsInstance(e, myokit.Plus) self.assertEqual(e.eval(), 7) e = p('5 - 2') self.assertIsInstance(e, myokit.Minus) self.assertEqual(e.eval(), 3) e = p('5 -- 2') self.assertIsInstance(e, myokit.Minus) self.assertEqual(e.eval(), 7) e = p('5 --+ 2') self.assertIsInstance(e, myokit.Minus) self.assertEqual(e.eval(), 7) e = p('5 --- 2') self.assertIsInstance(e, myokit.Minus) self.assertEqual(e.eval(), 3) # Etc etc etc # Test bad value self.assertRaises(myokit.ParseError, p, '5 beans')
def test_repr(self): # Test :meth:`Unit.repr()`. # Simple units m = myokit.parse_unit('m') self.assertEqual(repr(m), '[m]') im = 1 / m self.assertEqual(repr(im), '[1/m]') um = m * 1e-6 # Predefined complex unit self.assertEqual(repr(myokit.units.N), '[g*m/s^2 (1000)]') # Low multipliers self.assertEqual(repr(um), '[m (1e-06)]') um3 = myokit.parse_unit('um^3') self.assertEqual(repr(um3), '[m^3 (1e-18)]') # High multipliers nn = myokit.units.N * 1e-2 self.assertEqual(repr(nn), '[g*m/s^2 (10)]') nn = myokit.units.N * 1e7 self.assertEqual(repr(nn), '[g*m/s^2 (1e+10)]')
def cast(self, unit): """ Returns a new Quantity with this quantity's value and a different, possibly incompatible, unit. Example: >>> from myokit import Quantity as Q >>> a = Q('10 [A/F]') >>> b = a.cast('uA/cm^2') >>> print(str(b)) 10.0 [uA/cm^2] """ if not isinstance(unit, myokit.Unit): unit = myokit.parse_unit(unit) return Quantity(self._value, unit)
def setUpClass(cls): # CellML requires unit mapping units = { myokit.parse_unit('pF'): 'picofarad', } cls.w = cellml.CellMLExpressionWriter() cls.w.set_unit_function(lambda x: units[x]) model = myokit.Model() component = model.add_component('c') cls.avar = component.add_variable('a') # Requires valid model with unames set cls.avar.set_rhs(0) cls.avar.set_binding('time') model.validate() # MathML opening and closing tags cls._math = re.compile(r'^<math [^>]+>(.*)</math>$', re.S)
def test_set_unit(self): # Test :meth:`Variable.set_unit()`. m = myokit.Model() c = m.add_component('c') v = c.add_variable('v') # Test basic functionality s = myokit.UNIT_STRICT self.assertIsNone(v.unit()) self.assertEqual(v.unit(s), myokit.units.dimensionless) v.set_unit(myokit.units.Newton) self.assertEqual(v.unit(), myokit.units.Newton) self.assertEqual(v.unit(s), myokit.units.Newton) # Set via unit parsing v.set_unit('kg/ms') self.assertEqual(v.unit(), myokit.parse_unit('kg/ms')) # Set to a non unit self.assertRaisesRegex( TypeError, 'expects a myokit.Unit', v.set_unit, 12)
def create_one_comp_pk_model(): """ Returns 1 compartmental PK model. """ # Instantiate model model = Model() # add central compartment central_comp = model.add_compartment('central') # add variables and constants to central compartment amount = central_comp.add_variable('amount') volume = central_comp.add_variable('volume') k_e = central_comp.add_variable('k_e') conc = central_comp.add_variable('conc') # bind time time = central_comp.add_variable('time') time.set_binding('time') # set intial values (some default values) time.set_rhs(0) amount.set_rhs(0) volume.set_rhs(1) k_e.set_rhs(0) conc.set_rhs(0) # set units time.set_unit('day') # time in days amount.set_unit('mg') # miligram volume.set_unit('L') # liter k_e.set_unit('1 / day') # 1 / day conc.set_unit('mg / L') # miligram / liter # set preferred representation of units # time days unit = myokit.parse_unit('day') myokit.Unit.register_preferred_representation('day', unit) # rates in 1 / day unit = myokit.parse_unit('1/day') myokit.Unit.register_preferred_representation('1/day', unit) # amount in mg unit = myokit.parse_unit('mg') myokit.Unit.register_preferred_representation('mg', unit) # dose rate in mg / day unit = myokit.parse_unit('mg/day') myokit.Unit.register_preferred_representation('mg/day', unit) # concentration mg / L unit = myokit.parse_unit('mg/L') myokit.Unit.register_preferred_representation('mg/L', unit) # set rhs of state variables # (dot(amount) = - k_e * amount) amount.promote() amount.set_rhs( myokit.Multiply(myokit.PrefixMinus(myokit.Name(k_e)), myokit.Name(amount))) # set algebraic relation between drug and concentration conc.set_rhs(myokit.Divide(myokit.Name(amount), myokit.Name(volume))) return model
def test_parse_variable(self): """ Test parse_variable(), uses parse_expression() """ from myokit._parsing import parse_model as p # Test basics s = ( '[[model]]', '[x]', 't = 0 bind time', ) t = p(s).get('x.t') self.assertEqual(t.name(), 't') self.assertEqual(t.qname(), 'x.t') self.assertEqual(t.unit(), None) self.assertEqual(t.binding(), 'time') s = ( '[[model]]', '[x]', 't = 0', ' bind time', ) t = p(s).get('x.t') self.assertEqual(t.name(), 't') self.assertEqual(t.qname(), 'x.t') self.assertEqual(t.unit(), None) self.assertEqual(t.binding(), 'time') s = ('[[model]]', '[x]', 't = 0 bind time', 'x1 = 5') v = p(s).get('x.x1') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), None) self.assertEqual(v.rhs().unit(), None) s = ('[[model]]', '[x]', 't = 0 bind time', 'x2 = 5 [mV]') v = p(s).get('x.x2') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), None) self.assertEqual(v.rhs().unit(), myokit.parse_unit('mV')) s = ( '[[model]]', '[x]', 't = 0 bind time', 'x3 = 5', ' in [mV]', ) v = p(s).get('x.x3') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), myokit.parse_unit('mV')) self.assertEqual(v.rhs().unit(), None) s = ( '[[model]]', '[x]', 't = 0 bind time', 'x3 = 5 in [mV]', ) v = p(s).get('x.x3') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), myokit.parse_unit('mV')) self.assertEqual(v.rhs().unit(), None) s = ( '[[model]]', '[x]', 't = 0 bind time', 'x4 = 5 [V] : This is x4', ) v = p(s).get('x.x4') self.assertIsInstance(v, myokit.Variable) self.assertTrue(v.is_constant()) self.assertTrue(v.is_literal()) self.assertFalse(v.is_state()) self.assertFalse(v.is_intermediary()) self.assertEqual(v.unit(), None) self.assertEqual(v.rhs().unit(), myokit.units.V) self.assertEqual(v.meta['desc'], 'This is x4') code = ( '[[model]]', '[x]', 't = 0 bind time', 'x = 5 label vvv', ) x = p(code).get('x.x') self.assertIsInstance(x, myokit.Variable) self.assertEqual(x.label(), 'vvv') s = ( '[[model]]', '[x]', 't = 0 bind time', 'x = 5', ' label vvv', ) x = p(code).get('x.x') self.assertIsInstance(x, myokit.Variable) self.assertEqual(x.label(), 'vvv') # Illegal lhs code = ( '[[model]]', '[x]', 't = 0 bind time', 'sin(x) = 5', ) self.assertRaisesRegex(myokit.ParseError, 'variable names or the dot', p, code) # Duplicate name code = ( '[[model]]', '[x]', 't = 0 bind time', 'x = 5', 'x = 5', ) self.assertRaisesRegex(myokit.ParseError, 'Duplicate var', p, code) # Missing initial value code = ( '[[model]]', '[x]', 't = 0 bind time', 'dot(x) = 5', ) self.assertRaisesRegex(myokit.ParseError, 'Missing initial', p, code) # Duplicate meta code = ( '[[model]]', '[x]', 't = 0 bind time', ' yes: really', ' yes: no', ) self.assertRaisesRegex(myokit.ParseError, 'Duplicate meta-data key', p, code) # Duplicate unit code = ( '[[model]]', '[x]', 't = 0 bind time', ' in [s]', ' in [s]', ) self.assertRaisesRegex(myokit.ParseError, 'Duplicate variable unit', p, code)
def _rep(name): unit = myokit.parse_unit(name) myokit.Unit.register_preferred_representation(name, unit)
def create_tumour_growth_model(): r""" Returns a tumour growth myokit model. .. math:: \frac{\text{d}V^s_T}{\text{d}t} = \frac{2\lambda _0\lambda _1 V^s_T} {2\lambda _0 V^s_T + \lambda _1}, where the tumour volume :math:`V^s_T` is measured in :math:`\text{cm}^3`, the exponential growth rate :math:`\lambda _0` is mesured in :math:`\text{day}` and the linear growth rate :math:`\lambda _1` is measured in :math:`\text{cm}^3/\text{day}`. """ # Instantiate model model = Model() # add central compartment central_comp = model.add_compartment('central') # add tumour growth variables to central compartment volume_t = central_comp.add_variable('volume_t') lambda_0 = central_comp.add_variable('lambda_0') lambda_1 = central_comp.add_variable('lambda_1') # bind time time = central_comp.add_variable('time') time.set_binding('time') # set preferred representation of units # time in days unit = myokit.parse_unit('day') myokit.Unit.register_preferred_representation('day', unit) # rates in 1 / day unit = myokit.parse_unit('1/day') myokit.Unit.register_preferred_representation('1/day', unit) # tumor volume unit = myokit.parse_unit('cm^3') myokit.Unit.register_preferred_representation('cm^3', unit) # linear growth unit = myokit.parse_unit('cm^3/day') myokit.Unit.register_preferred_representation('cm^3/day', unit) # set intial values (some default values) and units time.set_rhs(0) volume_t.set_rhs(0) lambda_0.set_rhs(0) lambda_1.set_rhs(1) # avoid ZeroDivisionError # set units time.set_unit('day') # time in days volume_t.set_unit('cm^3') # milimeter cubed lambda_0.set_unit('1 / day') # per day lambda_1.set_unit('cm^3 / day') # milimiter cubed per day # set rhs of tumor volume # dot(volume_t) = # (2 * lambda_0 * lambda_1 * volume_t) / # (2 * lambda_0 * volume_t + lambda_1) volume_t.promote() volume_t.set_rhs( myokit.Divide( myokit.Multiply( myokit.Number(2), myokit.Multiply( myokit.Name(lambda_0), myokit.Multiply(myokit.Name(lambda_1), myokit.Name(volume_t)))), myokit.Plus( myokit.Multiply( myokit.Number(2), myokit.Multiply(myokit.Name(lambda_0), myokit.Name(volume_t))), myokit.Name(lambda_1)))) # Validate model model.validate() # TODO: Check units # model.check_units() return model
def conversion_factor(unit1, unit2, helpers=None): """ Returns a :class:`myokit.Quantity` ``c`` to convert from ``unit1`` to ``unit2``, such that ``1 [unit1] * c = 1 [unit2]``. For example:: >>> import myokit >>> myokit.Unit.conversion_factor('m', 'km') 0.001 [1 (1000)] Where:: 1 [m] = 0.001 [km/m] * 1 [km] so that ``c = 0.001 [km/m]``, and the unit ``[km/m]`` can be written as ``[km/m] = [ kilo ] = [1 (1000)]``. Conversions between incompatible units can be performed if one or multiple helper :class:`Quantity` objects are passed in. For example:: >>> import myokit >>> myokit.Unit.conversion_factor( ... 'uA/cm^2', 'uA/uF', ['1 [uF/cm^2]']) 1.0 [cm^2/uF] Where:: 1 [uA/cm^2] = 1 [cm^2/uF] * 1 [uA/uF] Note that this method uses the :meth:`close()` and :meth:`close_exponent` comparisons to see if units are equal. Arguments: ``unit1`` The new unit to convert from, given as a :class:`myokit.Unit` or as a string that can be converted to a unit with :meth:`myokit.parse_unit()`. ``unit2`` The new unit to convert to. ``helpers=None`` An optional list of conversion factors, which the method will attempt to use if the new and old units are incompatible. Each factor should be specified as a :class:`myokit.Quantity` or something that can be converted to a Quantity e.g. a string ``1 [uF/cm^2]``. Returns a :class:`myokit.Quantity`. Raises a :class:`myokit.IncompatibleUnitError` if the units cannot be converted.' """ # Check unit1 if not isinstance(unit1, myokit.Unit): if unit1 is None: unit1 = myokit.units.dimensionless else: unit1 = myokit.parse_unit(unit1) # Check unit2 if not isinstance(unit2, myokit.Unit): if unit2 is None: unit2 = myokit.units.dimensionless else: unit2 = myokit.parse_unit(unit2) # Check helper units factors = [] if helpers is not None: for factor in helpers: if not isinstance(factor, myokit.Quantity): factor = myokit.Quantity(factor) factors.append(factor) del (helpers) # Simplest case: units are (almost) equal if Unit.close(unit1, unit2): return Quantity(1) # Get conversion factor fw = None if Unit.close_exponent(unit1, unit2): # Directly convertible fw = 10**(unit1._m - unit2._m) else: # Try conversion via one of the helpers for factor in factors: unit1a = unit1 * factor.unit() if Unit.close_exponent(unit1a, unit2): fw = 10**(unit1a._m - unit2._m) * factor.value() break unit1a = unit1 / factor.unit() if Unit.close_exponent(unit1a, unit2): fw = 10**(unit1a._m - unit2._m) / factor.value() break # Unable to convert? if fw is None: msg = 'Unable to convert from ' + unit1.clarify() msg += ' to ' + unit2.clarify() if factors: msg += ' (even with help of conversion factors).' raise myokit.IncompatibleUnitError(msg + '.') # Create Quantity and return fw = myokit.float.round(fw) return Quantity(fw, unit2 / unit1)
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_convert_unit(self): # Test changing variable units m = myokit.parse_model(""" [[model]] membrane.V = -83 [env] t = 0 [ms] bind time in [ms] [membrane] dot(V) = - (ikr.i + ina.i * 1 [cm^2/uF]) in [mV] dotv = 5 [mV/ms] + dot(V) in [mV/ms] [cell] Cm = 0.123 [uF] in [uF] [ina] i = 12 [uA/cm^2] in [uA/cm^2] [ikr] use membrane.V E = -81 [mV] in [mV] g = 23 [mS/uF] in [mS/uF] i = g * (V - E) in [uA/uF] """) m.check_units(myokit.UNIT_STRICT) # Convert to same code = m.code() v = m.get('membrane.V') v.convert_unit('mV') self.assertEqual(code, m.code()) # Convert state vdot = v.rhs().eval() self.assertNotEqual(v.unit(), myokit.units.V) v.convert_unit('V') self.assertEqual(v.unit(), myokit.units.V) # Check dot(v) is the same, and all units are compatible vdot /= 1000 self.assertEqual(vdot, v.rhs().eval()) m.check_units(myokit.UNIT_STRICT) # Convert non-state i = m.get('ina.i') self.assertNotEqual(i.unit(), myokit.parse_unit('uA/uF')) i.convert_unit('uA/uF', ['1 [uF/cm^2]']) self.assertEqual(i.unit(), myokit.parse_unit('uA/uF')) # Check dot(v) is the same, and all units are compatible self.assertEqual(vdot, v.rhs().eval()) m.check_units(myokit.UNIT_STRICT) # Convert time variable t = m.time() self.assertNotEqual(t.unit(), myokit.units.s) t.convert_unit('s') self.assertEqual(t.unit(), myokit.units.s) # Check dot(v) is the same, and all units are compatible vdot *= 1000 self.assertEqual(vdot, v.rhs().eval()) m.check_units(myokit.UNIT_STRICT)
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 create_pktgi_model(): """ Returns 1 compartmental PK model. """ # Instantiate model model = Model() # Add central compartment central_comp = model.add_compartment('central') # Add PK variables and constants to central compartment amount = central_comp.add_variable('amount') volume_c = central_comp.add_variable('volume_c') k_e = central_comp.add_variable('k_e') conc = central_comp.add_variable('conc') # add PD variables to central compartment volume_t = central_comp.add_variable('volume_t') lambda_0 = central_comp.add_variable('lambda_0') lambda_1 = central_comp.add_variable('lambda_1') kappa = central_comp.add_variable('kappa') # bind time time = central_comp.add_variable('time') time.set_binding('time') # set intial values (some default values) time.set_rhs(0) amount.set_rhs(0) volume_c.set_rhs(1) # avoid ZeroDivisionError k_e.set_rhs(0) conc.set_rhs(0) volume_t.set_rhs(0) lambda_0.set_rhs(0) lambda_1.set_rhs(1) # avoid ZeroDivisionError kappa.set_rhs(0) # set units time.set_unit('day') # time in days amount.set_unit('mg') # miligram volume_c.set_unit('L') # liter k_e.set_unit('1 / day') # 1 / day conc.set_unit('mg / L') # miligram / liter volume_t.set_unit('cm^3') # milimeter cubed lambda_0.set_unit('1 / day') # per day lambda_1.set_unit('cm^3 / day') # milimiter cubed per day kappa.set_unit('L / mg / day') # in reference L / ug / day, # set preferred representation of units # time days unit = myokit.parse_unit('day') myokit.Unit.register_preferred_representation('day', unit) # rates in 1 / day unit = myokit.parse_unit('1/day') myokit.Unit.register_preferred_representation('1/day', unit) # amount in mg unit = myokit.parse_unit('mg') myokit.Unit.register_preferred_representation('mg', unit) # dose rate in mg / day unit = myokit.parse_unit('mg/day') myokit.Unit.register_preferred_representation('mg/day', unit) # concentration mg / L unit = myokit.parse_unit('mg/L') myokit.Unit.register_preferred_representation('mg/L', unit) # tumor volume unit = myokit.parse_unit('cm^3') myokit.Unit.register_preferred_representation('cm^3', unit) # linear growth unit = myokit.parse_unit('cm^3/day') myokit.Unit.register_preferred_representation('cm^3/day', unit) # potency unit = myokit.parse_unit('L/mg/day') myokit.Unit.register_preferred_representation('L/mg/day', unit) # set rhs of drug amount # (dot(amount) = - k_e * amount) amount.promote() amount.set_rhs( myokit.Multiply(myokit.PrefixMinus(myokit.Name(k_e)), myokit.Name(amount))) # set rhs of tumor volume # dot(volume_t) = # (2 * lambda_0 * lambda_1 * volume_t) / # (2 * lambda_0 * volume_t + lambda_1) - # kappa * conc * volume_t volume_t.promote() volume_t.set_rhs( myokit.Minus( myokit.Divide( myokit.Multiply( myokit.Number(2), myokit.Multiply( myokit.Name(lambda_0), myokit.Multiply(myokit.Name(lambda_1), myokit.Name(volume_t)))), myokit.Plus( myokit.Multiply( myokit.Number(2), myokit.Multiply(myokit.Name(lambda_0), myokit.Name(volume_t))), myokit.Name(lambda_1))), myokit.Multiply( myokit.Name(kappa), myokit.Multiply(myokit.Name(conc), myokit.Name(volume_t))))) # set algebraic relation between drug and concentration # conc = amount / volume_c conc.set_rhs(myokit.Divide(myokit.Name(amount), myokit.Name(volume_c))) return model