예제 #1
0
 def relog(_log, _dt):
     # Get first occurence of error
     ifirst, kfirst = find_error_position(_log)
     if kfirst is None:
         raise myokit.FindNanError('Error condition not found in log.')
     if ifirst == 0:
         raise myokit.FindNanError(
             'Unable to work with simulation logs where the error'
             ' condition is met in the very first data point.')
     # Position to start deep search at
     istart = ifirst - 1
     # Get last logged state before error
     state = []
     for dims in myokit.dimco(*self._dims):
         pre = '.'.join([str(x) for x in dims]) + '.'
         for s in self._model.states():
             state.append(_log[pre + s.qname()][istart])
     # Get last time before error
     time = _log[time_var][istart]
     # Save current state & time
     old_state = self._state
     old_time = self._time
     self._state = state
     self._time = time
     # Run until next time point, log every step
     duration = _log[time_var][ifirst] - time
     _log = self.run(duration,
                     log=myokit.LOG_BOUND + myokit.LOG_STATE,
                     log_interval=_dt,
                     report_nan=False)
     # Reset simulation to original state
     self._state = old_state
     self._time = old_time
     # Return new log
     return _log
예제 #2
0
 def relog(_logf, _logt, _dt):
     # Get first occurence of error
     ifirstf, kfirstf = find_error_position(_logf)
     ifirstt, kfirstt = find_error_position(_logt)
     if kfirstf is None and kfirstt is None:
         raise myokit.FindNanError('Error condition not found in logs.')
     elif kfirstf is None:
         ifirst = ifirstt
     elif kfirstt is None:
         ifirst = ifirstf
     elif ifirstf == 0 or ifirstt == 0:
         raise myokit.FindNanError(
             'Unable to work with simulation logs where the error'
             ' condition is met in the very first data point.')
     else:
         ifirst = min(ifirstf, ifirstt)
     # Position to start deep search at
     istart = ifirst - 1
     # Get last logged states before error
     statef = []
     statet = []
     for dims in myokit._dimco(*self._ncellsf):
         pre = '.'.join([str(x) for x in dims]) + '.'
         for s in self._modelf.states():
             statef.append(_logf[pre + s.qname()][istart])
     for dims in myokit._dimco(*self._ncellst):
         pre = '.'.join([str(x) for x in dims]) + '.'
         for s in self._modelt.states():
             statet.append(_logt[pre + s.qname()][istart])
     # Get last time before error
     time = _logf[time_varf][istart]
     # Save current state & time
     old_statef = self._statef
     old_statet = self._statet
     old_time = self._time
     self._statef = statef
     self._statet = statet
     self._time = time
     # Run until next time point, log every step
     duration = _logf[time_varf][ifirst] - time
     log = myokit.LOG_BOUND + myokit.LOG_STATE
     _logf, _logt = self.run(duration,
                             logf=log,
                             logt=log,
                             log_interval=_dt,
                             report_nan=False)
     # Reset simulation to original state
     self._statef = old_statef
     self._statet = old_statet
     self._time = old_time
     # Return new logs
     return _logf, _logt
예제 #3
0
    def find_nan(self, logf, logt):
        """
        Searches for the origin of a ``NaN`` (or ``inf``) in a set of
        simulation logs generated by this Simulation.

        The logs must contain the state of each cell and all bound variables.
        The NaN can occur at any point in time except the first.

        Returns a tuple ``(part, time, icell, variable, value, states, bound)``
        where ``time`` is the time the first ``NaN`` was found and ``icell`` is
        the index of the cell in which it happened. The entry ``part`` is a
        string containing either "fiber" or "tissue", indicating which part of
        the simulation triggered the error. The offending variable's name is
        given as ``variable`` and its (illegal) value as ``value``. The current
        state and, if available, any previous states are given in the list
        ``states``. Here, ``states[0]`` points to the current state in the
        simulation part causing the error, ``state[1]`` is the previous state
        and so on. Similarly the values of the error causing model's bound
        variables is given in ``bound``.
        """
        import numpy as np
        # Check if logs contain all states and bound variables
        lt = []
        lf = []
        for label in self._global:
            v = self._modelf.binding(label)
            if v is not None:
                lf.append(v.qname())
            v = self._modelt.binding(label)
            if v is not None:
                lt.append(v.qname())
        lf = myokit.prepare_log(myokit.LOG_STATE + myokit.LOG_BOUND,
                                self._modelf,
                                dims=self._ncellsf,
                                global_vars=lf)
        lt = myokit.prepare_log(myokit.LOG_STATE + myokit.LOG_BOUND,
                                self._modelt,
                                dims=self._ncellst,
                                global_vars=lt)
        for key in lf:
            if key not in logf:
                raise myokit.FindNanError(
                    'Method requires a simulation log from the fiber model'
                    ' containing all states and bound variables. Missing'
                    ' variable <' + key + '>.')
        for key in lt:
            if key not in logt:
                raise myokit.FindNanError(
                    'Method requires a simulation log from the tissue model'
                    ' containing all states and bound variables. Missing at'
                    ' least variable <' + key + '>.')
        del (lf)
        del (lt)

        # Error criterium: NaN/inf detection
        def bisect(ar, lo, hi):
            if not np.isfinite(ar[lo]):
                return lo
            md = lo + int(np.ceil(0.5 * (hi - lo)))
            if md == hi:
                return hi
            if not np.isfinite(ar[md]):
                return bisect(ar, lo, md)
            else:
                return bisect(ar, md, hi)

        # Search for first occurrence of propagating NaN in the log
        def find_error_position(log):
            ifirst = None  # Log list index
            kfirst = None  # Log key
            for key, ar in log.items():
                if ifirst is None:
                    if not np.isfinite(ar[-1]):
                        # First NaN found
                        kfirst = key
                        ifirst = bisect(ar, 0, len(ar) - 1)
                        if ifirst == 0:
                            break
                elif not np.isfinite(ar[ifirst - 1]):
                    # Earlier NaN found
                    kfirst = key
                    ifirst = bisect(ar, 0, ifirst)
                    if ifirst == 0:
                        break
            return ifirst, kfirst

        # Get the name of a time variable in the fiber model
        time_varf = self._modelf.time().qname()

        # Deep searching function
        def relog(_logf, _logt, _dt):
            # Get first occurence of error
            ifirstf, kfirstf = find_error_position(_logf)
            ifirstt, kfirstt = find_error_position(_logt)
            if kfirstf is None and kfirstt is None:
                raise myokit.FindNanError('Error condition not found in logs.')
            elif kfirstf is None:
                ifirst = ifirstt
            elif kfirstt is None:
                ifirst = ifirstf
            elif ifirstf == 0 or ifirstt == 0:
                raise myokit.FindNanError(
                    'Unable to work with simulation logs where the error'
                    ' condition is met in the very first data point.')
            else:
                ifirst = min(ifirstf, ifirstt)
            # Position to start deep search at
            istart = ifirst - 1
            # Get last logged states before error
            statef = []
            statet = []
            for dims in myokit._dimco(*self._ncellsf):
                pre = '.'.join([str(x) for x in dims]) + '.'
                for s in self._modelf.states():
                    statef.append(_logf[pre + s.qname()][istart])
            for dims in myokit._dimco(*self._ncellst):
                pre = '.'.join([str(x) for x in dims]) + '.'
                for s in self._modelt.states():
                    statet.append(_logt[pre + s.qname()][istart])
            # Get last time before error
            time = _logf[time_varf][istart]
            # Save current state & time
            old_statef = self._statef
            old_statet = self._statet
            old_time = self._time
            self._statef = statef
            self._statet = statet
            self._time = time
            # Run until next time point, log every step
            duration = _logf[time_varf][ifirst] - time
            log = myokit.LOG_BOUND + myokit.LOG_STATE
            _logf, _logt = self.run(duration,
                                    logf=log,
                                    logt=log,
                                    log_interval=_dt,
                                    report_nan=False)
            # Reset simulation to original state
            self._statef = old_statef
            self._statet = old_statet
            self._time = old_time
            # Return new logs
            return _logf, _logt

        # Get time step
        dt = logf[time_varf][1] - logf[time_varf][0]

        # Search with successively fine log interval
        while dt > 0:
            dt *= 0.1
            if dt < 0.5:
                dt = 0
            logf, logt = relog(logf, logt, dt)

        # Search for first occurrence of error in the detailed log
        ifirstf, kfirstf = find_error_position(logf)
        ifirstt, kfirstt = find_error_position(logt)
        if kfirstt is None or (kfirstf is not None and kfirstf < kfirstt):
            part = 'fiber'
            ifirst = ifirstf
            kfirst = kfirstf
            model = self._modelf
            log = logf
        else:
            part = 'tissue'
            ifirst = ifirstt
            kfirst = kfirstt
            model = self._modelt
            log = logt

        # Get indices of cell in state vector
        icell = [int(x) for x in kfirst.split('.')[0:2]]

        # Get state & bound before, during and after error
        def state(index, icell):
            s = []
            b = {}
            for var in model.states():
                s.append(log[var.qname(), icell][index])
            for var in model.variables(bound=True):
                if var.binding() in self._global:
                    b[var.qname()] = log[var.qname()][index]
                else:
                    b[var.qname()] = log[var.qname(), icell][index]
            return s, b

        # Get error cell's states before, during and after
        states = []
        bound = []
        max_states = 3
        for k in range(ifirst, ifirst - max_states - 1, -1):
            if k < 0:
                break
            s, b = state(k, icell)
            states.append(s)
            bound.append(b)

        # Get variable causing error
        var = model.get('.'.join(kfirst.split('.')[2:]))

        # Get value causing error
        value = states[1][var.indice()]
        var = var.qname()

        # Get time error occurred
        time = logf[time_varf][ifirst]

        # Return part, time, icell, variable, value, states, bound
        return part, time, icell, var, value, states, bound
예제 #4
0
    def find_nan(self, log, watch_var=None, safe_range=None):
        """
        Searches for the origin of a ``NaN`` (or ``inf``) in a simulation log
        generated by this Simulation.

        The log must contain the state of each cell and all bound variables.
        The NaN can occur at any point in time except the first.

        Returns a tuple ``(time, icell, variable, value, states, bound)`` where
        ``time`` is the time the first ``NaN`` was found and ``icell`` is the
        index of the cell in which it happened. The variable's name is given as
        ``variable`` and its (illegal) value as ``value``. The current state
        and, if available, any previous states are given in the list
        ``states``. Here, ``states[0]`` points to the current state,
        ``state[1]`` is the previous state and so on. Similarly the values of
        the model's bound variables is given in ``bound``.

        To aid in diagnosis, a variable can be selected as ``watch_var`` and a
        ``safe_range`` can be specified. With this option, the function will
        find and report either the first ``NaN`` or the first time the watched
        variable left the safe range, whatever came first. The safe range
        should be specified as ``(lower, upper)`` where both bounds are assumed
        to be in the safe range. The watched variable must be a state variable.
        """
        import numpy as np

        # Test if log contains all states and bound variables
        t = []
        for label in self._global:
            var = self._model.binding(label)
            if var is not None:
                t.append(var.qname())
        t = myokit.prepare_log(myokit.LOG_STATE + myokit.LOG_BOUND,
                               self._model,
                               dims=self._dims,
                               global_vars=t)
        for key in t:
            if key not in log:
                raise myokit.FindNanError(
                    'Method requires a simulation log containing all states'
                    ' and bound variables. Missing variable <' + key + '>.')
        del (t)

        # Error criterium
        if watch_var is None:

            # NaN/inf detection
            def bisect(ar, lo, hi):
                if not np.isfinite(ar[lo]):
                    return lo
                md = lo + int(np.ceil(0.5 * (hi - lo)))
                if md == hi:
                    return hi
                if not np.isfinite(ar[md]):
                    return bisect(ar, lo, md)
                else:
                    return bisect(ar, md, hi)

            def find_error_position(log):
                # Search for first occurrence of propagating NaN in the log
                ifirst = None
                kfirst = None
                for key, ar in log.iteritems():
                    if ifirst is None:
                        if not np.isfinite(ar[-1]):
                            # First NaN found
                            kfirst = key
                            ifirst = bisect(ar, 0, len(ar) - 1)
                            if ifirst == 0:
                                break
                    elif not np.isfinite(ar[ifirst - 1]):
                        # Earlier NaN found
                        kfirst = key
                        ifirst = bisect(ar, 0, ifirst)
                        if ifirst == 0:
                            break
                return ifirst, kfirst

        else:

            # Variable out of bounds detection
            try:
                watch_var = self._model.get(watch_var)
            except KeyError:
                raise myokit.FindNanError('Variable <' + str(watch_var) +
                                          '> not found.')
            if not watch_var.is_state():
                raise myokit.FindNanError(
                    'The watched variable must be a state.')
            try:
                lo, hi = safe_range
            except Exception:
                raise myokit.FindNanError(
                    'A safe range must be specified for the watched variable'
                    ' as a tuple (lower, upper).')
            if lo >= hi:
                raise myokit.FindNanError(
                    'The safe range must have a lower bound that is lower than'
                    ' the upper bound.')

            def find_error_position(_log):
                # Find first occurence of out-of-bounds error
                ifirst = None
                kfirst = None
                post = '.' + watch_var.qname()
                lower, upper = safe_range
                for dims in myokit.dimco(*self._dims):
                    key = '.'.join([str(x) for x in dims]) + post
                    ar = np.array(_log[key], copy=False)
                    i = np.where((ar < lower)
                                 | (ar > upper)
                                 | np.isnan(ar)
                                 | np.isinf(ar))[0]
                    if len(i) > 0:
                        i = i[0]
                        if ifirst is None:
                            kfirst = key
                            ifirst = i
                        elif i < ifirst:
                            kfirst = key
                            ifirst = i
                        if i == 0:
                            break
                return ifirst, kfirst

        # Get the name of a time variable
        time_var = self._model.time().qname()

        # Deep searching function
        def relog(_log, _dt):
            # Get first occurence of error
            ifirst, kfirst = find_error_position(_log)
            if kfirst is None:
                raise myokit.FindNanError('Error condition not found in log.')
            if ifirst == 0:
                raise myokit.FindNanError(
                    'Unable to work with simulation logs where the error'
                    ' condition is met in the very first data point.')
            # Position to start deep search at
            istart = ifirst - 1
            # Get last logged state before error
            state = []
            for dims in myokit.dimco(*self._dims):
                pre = '.'.join([str(x) for x in dims]) + '.'
                for s in self._model.states():
                    state.append(_log[pre + s.qname()][istart])
            # Get last time before error
            time = _log[time_var][istart]
            # Save current state & time
            old_state = self._state
            old_time = self._time
            self._state = state
            self._time = time
            # Run until next time point, log every step
            duration = _log[time_var][ifirst] - time
            _log = self.run(duration,
                            log=myokit.LOG_BOUND + myokit.LOG_STATE,
                            log_interval=_dt,
                            report_nan=False)
            # Reset simulation to original state
            self._state = old_state
            self._time = old_time
            # Return new log
            return _log

        # Get time step
        try:
            dt = log[time_var][1] - log[time_var][0]
        except IndexError:
            # Unable to guess dt!
            # So... Nan occurs before the first log interval is reached
            # That probably means dt was relatively large, so guess it was
            # large! Assuming milliseconds, start off with dt=5ms
            dt = 5

        # Search with successively fine log interval
        while dt > 0:
            dt *= 0.1
            if dt < 0.5:
                dt = 0
            log = relog(log, dt)

        # Search for first occurrence of error in the detailed log
        ifirst, kfirst = find_error_position(log)

        # Get indices of cell in state vector
        ndims = len(self._dims)
        icell = [int(x) for x in kfirst.split('.')[0:ndims]]

        # Get state & bound before, during and after error
        def state(index, icell):
            s = []
            b = {}
            for var in self._model.states():
                s.append(log[var.qname(), icell][index])
            for var in self._model.variables(bound=True):
                if var.binding() in self._global:
                    b[var.qname()] = log[var.qname()][index]
                else:
                    b[var.qname()] = log[var.qname(), icell][index]
            return s, b

        # Get error cell's states before, during and after
        states = []
        bound = []
        max_states = 3
        for k in xrange(ifirst, ifirst - max_states - 1, -1):
            if k < 0:
                break
            s, b = state(k, icell)
            states.append(s)
            bound.append(b)

        # Get variable causing error
        var = self._model.get('.'.join(kfirst.split('.')[ndims:]))

        # Get value causing error
        value = states[1][var.indice()]
        var = var.qname()

        # Get time error occurred
        time = log[time_var][ifirst]

        # Return time, icell, variable, value, states, bound
        return time, icell, var, value, states, bound