Ejemplo n.º 1
0
    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))
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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()
Ejemplo n.º 5
0
    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