Esempio n. 1
0
 def _run(self, duration, log, log_interval, apd_threshold, progress, msg):
     # Reset error state
     self._error_state = None
     # Simulation times
     if duration < 0:
         raise Exception('Simulation time can\'t be negative.')
     tmin = self._time
     tmax = tmin + duration
     # Parse log argument
     log = myokit.prepare_log(log, self._model, if_empty=myokit.LOG_ALL)
     # Logging period (0 = disabled)
     log_interval = 0 if log_interval is None else float(log_interval)
     if log_interval < 0:
         log_interval = 0
     # Threshold for APD measurement
     root_list = None
     root_threshold = 0
     if apd_threshold is not None:
         if self._apd_var is None:
             raise ValueError('Threshold given but Simulation object was'
                              ' created without apd_var argument.')
         else:
             root_list = []
             root_threshold = float(apd_threshold)
     # Get progress indication function (if any)
     if progress is None:
         progress = myokit._Simulation_progress
     if progress:
         if not isinstance(progress, myokit.ProgressReporter):
             raise ValueError(
                 'The argument "progress" must be either a'
                 ' subclass of myokit.ProgressReporter or None.')
     # Determine benchmarking mode, create time() function if needed
     if self._model.binding('realtime') is not None:
         import timeit
         bench = timeit.default_timer
     else:
         bench = None
     # Run simulation
     with myokit.SubCapture():
         arithmetic_error = None
         if duration > 0:
             # Initialize
             state = [0] * len(self._state)
             bound = [0, 0, 0, 0]  # time, pace, realtime, evaluations
             self._sim.sim_init(
                 tmin,
                 tmax,
                 list(self._state),
                 state,
                 bound,
                 self._protocol,
                 self._fixed_form_protocol,
                 log,
                 log_interval,
                 root_list,
                 root_threshold,
                 bench,
             )
             t = tmin
             try:
                 if progress:
                     # Loop with feedback
                     with progress.job(msg):
                         r = 1.0 / duration if duration != 0 else 1
                         while t < tmax:
                             t = self._sim.sim_step()
                             if not progress.update(min((t - tmin) * r, 1)):
                                 raise myokit.SimulationCancelledError()
                 else:
                     # Loop without feedback
                     while t < tmax:
                         t = self._sim.sim_step()
             except ArithmeticError as ea:
                 self._error_state = list(state)
                 txt = [
                     'A numerical error occurred during simulation at'
                     ' t = ' + str(t) + '.', 'Last reached state: '
                 ]
                 txt.extend([
                     '  ' + x
                     for x in self._model.format_state(state).splitlines()
                 ])
                 txt.append('Inputs for binding: ')
                 txt.append('  time        = ' + myokit.strfloat(bound[0]))
                 txt.append('  pace        = ' + myokit.strfloat(bound[1]))
                 txt.append('  realtime    = ' + myokit.strfloat(bound[2]))
                 txt.append('  evaluations = ' + myokit.strfloat(bound[3]))
                 try:
                     self._model.eval_state_derivatives(state)
                 except myokit.NumericalError as en:
                     txt.append(en.message)
                 raise myokit.SimulationError('\n'.join(txt))
             except Exception as e:
                 # Store error state
                 self._error_state = list(state)
                 # Check for zero-step error
                 if e.message[0:9] == 'ZERO_STEP':
                     time = float(e.message[10:])
                     raise myokit.SimulationError('Too many failed steps at'
                                                  ' t=' + str(time))
                 # Unhandled exception: re-raise!
                 raise
             finally:
                 # Clean even after KeyboardInterrupt or other Exception
                 self._sim.sim_clean()
             # Update internal state
             self._state = state
     # Return
     if root_list is not None:
         # Calculate apds and return (log, apds)
         st = []
         dr = []
         if root_list:
             roots = iter(root_list)
             time, direction = roots.next()
             tlast = time if direction > 0 else None
             for time, direction in roots:
                 if direction > 0:
                     tlast = time
                 else:
                     st.append(tlast)
                     dr.append(time - tlast)
         apds = myokit.DataLog()
         apds['start'] = st
         apds['duration'] = dr
         return log, apds
     else:
         # Return log
         return log
Esempio n. 2
0
    def _compile(
            self, name, tpl, tpl_vars, libs, libd=None, incd=None, flags=None):
        """
        Compiles a source template into a module and returns it.

        The module's name is specified by ``name``.

        The template to compile is given by ``tpl``, while any variables
        required to process the template should be given as the dict
        ``tpl_vars``.

        Any C libraries needed for compilation should be given in the sequence
        type ``libs``. Library dirs and include dirs can be passed in using
        ``libd`` and ``incd``. Extra compiler arguments can be given in the
        list ``flags``.
        """
        src_file = self._source_file()
        d_cache = tempfile.mkdtemp('myokit')
        try:
            # Create output directories
            d_build = os.path.join(d_cache, 'build')
            d_modul = os.path.join(d_cache, 'module')
            os.makedirs(d_build)
            os.makedirs(d_modul)

            # Export c file
            src_file = os.path.join(d_cache, src_file)
            self._export(tpl, tpl_vars, src_file)

            # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on
            # unconventional linux sundials installations, but not on windows
            # as this can lead to a weird error in setuptools
            runtime = None if platform.system() == 'Windows' else libd

            # Create extension
            ext = Extension(
                name,
                sources=[src_file],
                libraries=libs,
                library_dirs=libd,
                runtime_library_dirs=runtime,
                include_dirs=incd,
                extra_compile_args=flags,
            )

            # Compile, catch output
            with myokit.SubCapture() as s:
                try:
                    setup(
                        name=name,
                        description='Temporary module',
                        ext_modules=[ext],
                        script_args=[
                            'build', '--build-base=' + d_build,
                            'install', '--install-lib=' + d_modul,
                        ])
                except (Exception, SystemExit) as e:
                    s.disable()
                    t = [
                        'Unable to compile.',
                        'Error message:',
                        '    ' + e.message,
                        'Error traceback',
                        traceback.format_exc(),
                        'Compiler output:',
                    ]
                    captured = s.text().strip()
                    t.extend(['    ' + x for x in captured.splitlines()])
                    raise myokit.CompilationError('\n'.join(t))

            # Include module (and refresh in case 2nd model is loaded)
            (f, pathname, description) = imp.find_module(name, [d_modul])
            return imp.load_dynamic(name, pathname)

        finally:
            try:
                shutil.rmtree(d_cache)
            except Exception:
                pass
    def _run(self, duration, log, log_interval, log_times, apd_threshold,
             progress, msg):

        # Reset error state
        self._error_state = None

        # Simulation times
        if duration < 0:
            raise ValueError('Simulation time can\'t be negative.')
        tmin = self._time
        tmax = tmin + duration

        # Parse log argument
        log = myokit.prepare_log(log, self._model, if_empty=myokit.LOG_ALL)

        # Logging period (None or 0 = disabled)
        log_interval = 0 if log_interval is None else float(log_interval)
        if log_interval < 0:
            log_interval = 0

        # Logging points (None or empty list = disabled)
        if log_times is not None:
            log_times = [float(x) for x in log_times]
            if len(log_times) == 0:
                log_times = None
            else:
                # Allow duplicates, but always non-decreasing!
                import numpy as np
                x = np.asarray(log_times)
                if np.any(x[1:] < x[:-1]):
                    raise ValueError(
                        'Values in log_times must be non-decreasing.')
                del (x, np)
        if log_times is not None and log_interval > 0:
            raise ValueError(
                'The arguments log_times and log_interval cannot be used'
                ' simultaneously.')

        # Threshold for APD measurement
        root_list = None
        root_threshold = 0
        if apd_threshold is not None:
            if self._apd_var is None:
                raise ValueError('Threshold given but Simulation object was'
                                 ' created without apd_var argument.')
            else:
                root_list = []
                root_threshold = float(apd_threshold)

        # Get progress indication function (if any)
        if progress is None:
            progress = myokit._Simulation_progress
        if progress:
            if not isinstance(progress, myokit.ProgressReporter):
                raise ValueError(
                    'The argument "progress" must be either a'
                    ' subclass of myokit.ProgressReporter or None.')

        # Determine benchmarking mode, create time() function if needed
        if self._model.binding('realtime') is not None:
            import timeit
            bench = timeit.default_timer
        else:
            bench = None

        # Run simulation
        with myokit.SubCapture() as capture:
            if duration > 0:
                # Initialize
                state = [0] * len(self._state)
                bound = [0, 0, 0, 0]  # time, pace, realtime, evaluations
                self._sim.sim_init(
                    tmin,
                    tmax,
                    list(self._state),
                    state,
                    bound,
                    self._protocol,
                    self._fixed_form_protocol,
                    log,
                    log_interval,
                    log_times,
                    root_list,
                    root_threshold,
                    bench,
                )
                t = tmin
                try:
                    if progress:
                        # Allow progress reporters to bypass the subcapture
                        progress._set_output_stream(capture.bypass())
                        # Loop with feedback
                        with progress.job(msg):
                            r = 1.0 / duration if duration != 0 else 1
                            while t < tmax:
                                t = self._sim.sim_step()
                                if not progress.update(min((t - tmin) * r, 1)):
                                    raise myokit.SimulationCancelledError()
                    else:
                        # Loop without feedback
                        while t < tmax:
                            t = self._sim.sim_step()
                except ArithmeticError:
                    self._error_state = list(state)
                    txt = [
                        'A numerical error occurred during simulation at'
                        ' t = ' + str(t) + '.', 'Last reached state: '
                    ]
                    txt.extend([
                        '  ' + x
                        for x in self._model.format_state(state).splitlines()
                    ])
                    txt.append('Inputs for binding: ')
                    txt.append('  time        = ' + myokit.strfloat(bound[0]))
                    txt.append('  pace        = ' + myokit.strfloat(bound[1]))
                    txt.append('  realtime    = ' + myokit.strfloat(bound[2]))
                    txt.append('  evaluations = ' + myokit.strfloat(bound[3]))
                    try:
                        self._model.eval_state_derivatives(state)
                    except myokit.NumericalError as en:
                        txt.append(str(en))
                    raise myokit.SimulationError('\n'.join(txt))
                except Exception as e:

                    # Store error state
                    self._error_state = list(state)

                    # Check for known CVODE errors
                    if 'Function CVode()' in str(e):
                        raise myokit.SimulationError(str(e))

                    # Check for zero step error
                    if str(e)[:10] == 'ZERO_STEP ':  # pragma: no cover
                        t = float(str(e)[10:])
                        raise myokit.SimulationError(
                            'Maximum number of zero-size steps made at t=' +
                            str(t))

                    # Unknown exception: re-raise!
                    raise
                finally:
                    # Clean even after KeyboardInterrupt or other Exception
                    self._sim.sim_clean()
                # Update internal state
                self._state = state

        # Return
        if root_list is not None:
            # Calculate apds and return (log, apds)
            st = []
            dr = []
            if root_list:
                roots = iter(root_list)
                time, direction = next(roots)
                tlast = time if direction > 0 else None
                for time, direction in roots:
                    if direction > 0:
                        tlast = time
                    else:
                        st.append(tlast)
                        dr.append(time - tlast)
            apds = myokit.DataLog()
            apds['start'] = st
            apds['duration'] = dr
            return log, apds
        else:
            # Return log
            return log
    def _compile(self,
                 name,
                 tpl,
                 tpl_vars,
                 libs,
                 libd=None,
                 incd=None,
                 flags=None):
        """
        Compiles a source template into a module and returns it.

        The module's name is specified by ``name``.

        The template to compile is given by ``tpl``, while any variables
        required to process the template should be given as the dict
        ``tpl_vars``.

        Any C libraries needed for compilation should be given in the sequence
        type ``libs``. Library dirs and include dirs can be passed in using
        ``libd`` and ``incd``. Extra compiler arguments can be given in the
        list ``flags``.
        """
        src_file = self._source_file()
        d_cache = tempfile.mkdtemp('myokit')
        try:
            # Create output directories
            d_build = os.path.join(d_cache, 'build')
            d_modul = os.path.join(d_cache, 'module')
            os.makedirs(d_build)
            os.makedirs(d_modul)

            # Export c file
            src_file = os.path.join(d_cache, src_file)
            self._export(tpl, tpl_vars, src_file)

            # Inputs must all be strings
            name = str(name)
            src_file = str(src_file)
            flags = None if flags is None else [str(x) for x in flags]
            libd = None if libd is None else [str(x) for x in libd]
            incd = None if incd is None else [str(x) for x in incd]
            libs = None if libs is None else [str(x) for x in libs]

            # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on
            # unconventional linux sundials installations, but not on windows
            # as this can lead to a weird error in setuptools
            runtime = libd
            if (platform.system() == 'Windows'
                    and libd is not None):  # pragma: no linux cover
                runtime = None

                # Instead, add libd to path on windows
                try:
                    path = os.environ['path']
                except KeyError:
                    path = ''
                to_add = [x for x in libd if x not in path]
                if to_add:
                    os.environ['path'] = os.pathsep.join([path] + to_add)

            # Create extension
            ext = Extension(
                name,
                sources=[src_file],
                libraries=libs,
                library_dirs=libd,
                runtime_library_dirs=runtime,
                include_dirs=incd,
                extra_compile_args=flags,
            )

            # Compile, catch output
            with myokit.SubCapture() as s:
                try:
                    setup(name=name,
                          description='Temporary module',
                          ext_modules=[ext],
                          script_args=[
                              str('build'),
                              str('--build-base=' + d_build),
                              str('install'),
                              str('--install-lib=' + d_modul),
                              str('--old-and-unmanageable'),
                          ])
                except (Exception, SystemExit) as e:
                    s.disable()
                    t = ['Unable to compile.', 'Error message:']
                    t.append(str(e))
                    t.append('Error traceback')
                    t.append(traceback.format_exc())
                    t.append('Compiler output:')
                    captured = s.text().strip()
                    t.extend(['    ' + x for x in captured.splitlines()])
                    raise myokit.CompilationError('\n'.join(t))
                finally:
                    egg = name + '.egg-info'
                    if os.path.exists(egg):
                        shutil.rmtree(egg)

            # Include module (and refresh in case 2nd model is loaded)
            return load_module(name, d_modul)

        finally:
            try:
                shutil.rmtree(d_cache)
            except Exception:  # pragma: no cover
                pass
Esempio n. 5
0
    def _compile(self,
                 name,
                 tpl,
                 tpl_vars,
                 libs,
                 libd=None,
                 incd=None,
                 carg=None,
                 larg=None):
        """
        Compiles a source template into a module and returns it.

        The module's name is specified by ``name``.

        The template to compile is given by ``tpl``, while any variables
        required to process the template should be given as the dict
        ``tpl_vars``.

        Any C libraries needed for compilation should be given in the sequence
        type ``libs``. Library dirs and include dirs can be passed in using
        ``libd`` and ``incd``. Extra compiler arguments can be given in the
        list ``carg``, and linker args in ``larg``.
        """
        src_file = self._source_file()
        working_dir = os.getcwd()
        d_cache = tempfile.mkdtemp('myokit')
        try:
            # Create output directories
            d_build = os.path.join(d_cache, 'build')
            os.makedirs(d_build)

            # Export c file
            src_file = os.path.join(d_cache, src_file)
            self._export(tpl, tpl_vars, src_file)

            # Ensure headers can be read from myokit/_sim
            if incd is None:
                incd = []
            incd.append(myokit.DIR_CFUNC)

            # Inputs must all be strings
            name = str(name)
            src_file = str(src_file)
            incd = [str(x) for x in incd]
            libd = None if libd is None else [str(x) for x in libd]
            libs = None if libs is None else [str(x) for x in libs]
            carg = None if carg is None else [str(x) for x in carg]
            larg = None if larg is None else [str(x) for x in larg]

            # Uncomment to debug C89 issues
            '''
            if carg is None:
                carg = []
            carg.extend([
                #'-Wall',
                #'-Wextra',
                #'-Werror=strict-prototypes',
                #'-Werror=old-style-definition',
                #'-Werror=missing-prototypes',
                #'-Werror=missing-declarations',
                '-Werror=declaration-after-statement',
            ])
            #'''

            # Add runtime_library_dirs (to prevent LD_LIBRARY_PATH) errors on
            # unconventional linux sundials installations, but not on windows
            # as this can lead to a weird error in setuptools
            runtime = libd
            if platform.system() == 'Windows':  # pragma: no linux cover
                if libd is not None:
                    runtime = None

                    # Determine strategy
                    try:
                        os.add_dll_directory
                        use_add_dll_directory = True
                    except AttributeError:
                        use_add_dll_directory = False

                    # Make windows search the libd directories
                    if use_add_dll_directory:
                        # Python 3.8 and up
                        for path in libd:
                            if os.path.isdir(path):
                                os.add_dll_directory(path)

                    else:
                        # Older versions: add libd to path
                        path = os.environ.get('path', '')
                        if path is None:
                            path = ''
                        to_add = [x for x in libd if x not in path]
                        os.environ['path'] = os.pathsep.join([path] + to_add)

            # Create extension
            ext = Extension(
                name,
                sources=[src_file],
                libraries=libs,
                library_dirs=libd,
                runtime_library_dirs=runtime,
                include_dirs=incd,
                extra_compile_args=carg,
                extra_link_args=larg,
            )

            # Compile in build directory, catch output
            with myokit.SubCapture() as s:
                try:
                    os.chdir(d_build)
                    setup(name=name,
                          description='Temporary module',
                          ext_modules=[ext],
                          script_args=[
                              str('build_ext'),
                              str('--inplace'),
                          ])
                except (Exception, SystemExit) as e:  # pragma: no cover
                    s.disable()
                    t = ['Unable to compile.', 'Error message:']
                    t.append(str(e))
                    t.append('Error traceback')
                    t.append(traceback.format_exc())
                    t.append('Compiler output:')
                    captured = s.text().strip()
                    t.extend(['    ' + x for x in captured.splitlines()])
                    raise myokit.CompilationError('\n'.join(t))

            # Include module (and refresh in case 2nd model is loaded)
            return load_module(name, d_build)

        finally:
            # Revert changes to working directory
            os.chdir(working_dir)

            # Delete cached module
            try:
                myokit._rmtree(d_cache)
            except Exception:  # pragma: no cover
                pass