def test_float_functions(self): # Test the floating point comparison methods # Test that test is going to work x = 49 y = 1 / (1 / x) self.assertNotEqual(x, y) # Test if feq allows 1 floating point error # Test if self.assertTrue(myokit._feq(x, y)) self.assertTrue(myokit._fgeq(x, y)) self.assertTrue(myokit._fgeq(y, x)) x += x * sys.float_info.epsilon self.assertTrue(myokit._feq(x, y)) self.assertTrue(myokit._fgeq(x, y)) self.assertTrue(myokit._fgeq(y, x)) x += x * sys.float_info.epsilon self.assertFalse(myokit._feq(x, y)) self.assertTrue(myokit._fgeq(x, y)) self.assertFalse(myokit._fgeq(y, x)) # Test rounding self.assertNotEqual(49, y) self.assertEqual(49, myokit._fround(y)) self.assertNotEqual(49, myokit._fround(x)) self.assertEqual(0.5, myokit._fround(0.5)) self.assertIsInstance(myokit._fround(y), int) # Try with negative numbers self.assertNotEqual(-49, -y) self.assertEqual(-49, myokit._fround(-y)) self.assertNotEqual(-49, myokit._fround(-x)) self.assertEqual(-0.5, myokit._fround(-0.5)) # Test that _close allows bigger errors x = 49 y = x * (1 + 1e-11) self.assertNotEqual(x, y) self.assertFalse(myokit._feq(x, y)) self.assertTrue(myokit._close(x, y)) # And that close thinks everything small is equal x = 1e-16 y = 1e-12 self.assertNotEqual(x, y) self.assertFalse(myokit._feq(x, y)) self.assertTrue(myokit._close(x, y)) # Test rounding based on closeness x = 49 y = x * (1 + 1e-11) self.assertNotEqual(x, y) self.assertEqual(x, myokit._cround(y)) self.assertIsInstance(myokit._cround(y), int) self.assertNotEqual(x, myokit._cround(49.001))
def is_sequence_exception(self): """ Like :meth:`is_sequence()`, but raises an exception if the protocol is not a sequence, providing some information about the check that failed. """ t = 0 e = self._head while e is not None: if e._period != 0: raise NotASequenceError('Protocol contains periodic event(s).') if e._start < t: raise NotASequenceError( 'Event starting at t=' + str(e._start) + ' overlaps with previous event which finishes at t=' + str(t) + '.') t = e._start + e._duration e = e._next # Calculated position indistinguishable from user-specified next # even start? Then jump there instead if e and myokit._feq(t, e._start): t = e._start return True
def advance(self, new_time): """ Advances the time in the pacing system to ``new_time``. Returns the current value of the pacing variable. """ # Check new_time isn't in the past new_time = float(new_time) if new_time < self._time: raise ValueError('New time cannot be before the current time.') # Set the new internal time self._time = new_time # Advance pacing system while myokit._fgeq(new_time, self._tnext): # Active event finished if self._fire and myokit._fgeq(self._tnext, self._tdown): self._fire = None self._pace = 0 # New event starting e = self._protocol._head if e and myokit._fgeq(new_time, e._start): self._protocol.pop() self._fire = e self._tdown = e._start + e._duration self._pace = e._level # Reschedule recurring events if e._period > 0 and e._multiplier != 1: if e._multiplier > 1: e._multiplier -= 1 e._start += e._period self._protocol.add(e) # Check if tdown is indistinguishable from the next event start # If so, then set tdown (which is always calculated) to the # next event start (which may be user-specified). x = self._protocol._head if x and myokit._feq(self._tdown, x._start): self._tdown = x._start # Next stopping time self._tnext = float('inf') if self._fire and self._tnext > self._tdown: self._tnext = self._tdown if e and self._tnext > e._start: self._tnext = e._start return self._pace
def _units(self, parent, units): """ Adds a ``units`` element to ``parent``, for the given CellML units object. """ # Get unit exponents on first call if self._exp_si is None: from myokit.formats.cellml import v2 self._exp_si = [ v2.Units._si_units_r[x] for x in myokit.Unit.list_exponents() ] # Create units element element = etree.SubElement(parent, 'units') element.attrib['name'] = units.name() # Get myokit unit myokit_unit = units.myokit_unit() # Add unit row for each of the 7 SI units needed to make up this unit rows = [] for k, e in enumerate(myokit_unit.exponents()): if e != 0: row = etree.SubElement(element, 'unit') row.attrib['units'] = self._exp_si[k] if e != 1: row.attrib['exponent'] = str(e) # Must be an integer rows.append(row) # Handle dimensionless units with a multiplier if not rows: row = etree.SubElement(element, 'unit') row.attrib['units'] = 'dimensionless' rows.append(row) # Add multiplier or prefix to first row multiplier = myokit_unit.multiplier() if multiplier != 1: if myokit._feq(multiplier, int(multiplier)): rows[0].attrib['multiplier'] = str(int(multiplier)) else: rows[0].attrib['multiplier'] = myokit.strfloat( multiplier).strip()
def is_unbroken_sequence_exception(self): """ Like :meth:`is_unbroken_sequence`, but raises an exception if the protocol is not an unbroken sequence, providing some information about the check that failed. """ e = self._head if e is None: return True if e._period != 0: raise NotAnUnbrokenSequenceError( 'Protocol contains periodic event(s).') while e._next is not None: t = e._start + e._duration e = e._next # Calculated position indistinguishable from user-specified next # even start? Then jump there instead if myokit._feq(t, e._start): t = e._start # Check for periodic events if e._period != 0: raise NotAnUnbrokenSequenceError( 'Protocol contains periodic event(s).') # Check starting time if e._start < t: raise NotAnUnbrokenSequenceError( 'Event starting at t=' + str(e._start) + ' overlaps with previous event which finishes at t=' + str(t) + '.') elif e._start > t: raise NotAnUnbrokenSequenceError( 'Event starting at t=' + str(e._start) + ' does not start directly after previous event,' + ' which finishes at t=' + str(t) + '.') return True