示例#1
0
    def init(self) -> SimPhase:
        '''
        Initialize this simulation with the cell cluster seeded by a prior call
        to the :meth:`seed` method and cache this initialization to an output
        file, specified by the current configuration file.

        This method *must* be called prior to the :meth:`sim` and
        :meth:`plot_init` methods, which consume this output as input.

        Returns
        ----------
        SimPhase
            High-level simulation phase instance encapsulating all objects
            internally created by this method to run this phase.
        '''

        # Log this attempt.
        logs.log_info('Initializing simulation...')

        # Simulation phase type.
        phase_kind = SimPhaseKind.INIT

        if not files.is_file(self._p.seed_pickle_filename):
            if not self._p.autoInit:
                #FIXME: Call the _die_unless_file_pickled() function instead
                #both here and everywhere else we perform similar logic. Bugbears!
                raise BetseSimException(
                    'Initialization halted due to missing seed. '
                    'Please run "betse seed" and try again.')

            # Create an instance of world.
            logs.log_info('Automatically seeding cell cluster...')
            self.seed()
            logs.log_info('Now using seed to run initialization.')

        # Load the seed from cache.
        cells, p_old = fh.loadWorld(self._p.seed_pickle_filename)
        logs.log_info('Cell cluster loaded.')

        # check to ensure compatibility between original and present sim files:
        self._die_if_seed_differs(p_old, self._p)

        # Simulation phase, created *AFTER* unpickling these objects above.
        phase = SimPhase(
            kind=phase_kind,
            cells=cells,
            p=self._p,
            callbacks=self._callbacks,
        )

        # Initialize core simulation data structures.
        phase.sim.init_core(phase)

        # Run this simulation phase.
        phase.sim.sim_info_report(phase)
        phase.sim.run_sim_core(phase)

        # Return this phase.
        return phase
示例#2
0
    def sim(self) -> SimPhase:
        '''
        Simulate this simulation with the cell cluster initialized by a prior
        call to the :meth:`init` method and cache this simulation to an output
        file, specified by the current configuration file.

        This method *must* be called prior to the :meth:`:meth:`plot_sim`
        method, which consumes this output as input.

        Returns
        ----------
        SimPhase
            High-level simulation phase instance encapsulating all objects
            internally created by this method to run this phase.
        '''

        # Log this attempt.
        logs.log_info('Running simulation...')

        # Simulation phase type.
        phase_kind = SimPhaseKind.SIM

        if not files.is_file(self._p.init_pickle_filename):
            if not self._p.autoInit:
                raise BetseSimException(
                    'Simulation halted due to missing initialization. '
                    'Please run "betse init" and try again.')

            logs.log_info('Automatically initializing cell cluster...')
            self.init()
            logs.log_info('Now using initialization to run simulation.')

        # Load the initialization from cache.
        sim, cells, p_old = fh.loadSim(self._p.init_pickle_filename)

        # Ensure compatibility between original and present config files.
        self._die_if_seed_differs(p_old, self._p)

        # Simulation phase, created *AFTER* unpickling these objects above.
        phase = SimPhase(
            kind=phase_kind,
            cells=cells,
            p=self._p,
            sim=sim,
            callbacks=self._callbacks,
        )

        # Run and save the simulation to the cache.
        sim.sim_info_report(phase)
        sim.run_sim_core(phase)

        # Return this phase.
        return phase
示例#3
0
def is_dll(pathname: str) -> bool:
    '''
    ``True`` only if the passed path is that of an existing shared library
    specific to the current platform (e.g., suffixed by ``.so`` on Linux).

    This function returns ``False`` if either:

    * This path is *not* that of an existing file.
    * This pathname has no filetype.
    * This pathname's filetype is *not* that of a shared library supported by
      the current platform.

    Parameters
    ----------
    pathname : str
        Absolute or relative path of the shared library to inspect.

    Returns
    ----------
    bool
        ``True`` only if this path is that of an existing shared library.
    '''

    # Avoid circular import dependencies.
    from betse.util.path import files, pathnames
    from betse.util.os import kernels

    # Filetype of this pathname if any or None otherwise.
    filetype = pathnames.get_filetype_undotted_or_none(pathname)

    # Name of this platform's low-level kernel (e.g., "Linux", "Darwin").
    kernel_name = kernels.get_name()

    # This path is that of an existing shared library if and only if...
    return (
        # This pathname has a filetype.
        filetype is not None and
        # This filetype is that of a shared library for this platform.
        filetype in KERNEL_NAME_TO_DLL_FILETYPES[kernel_name] and
        # This path is that of an existing file. Since testing this requires a
        # filesystem lookup, this is the slowest test and thus deferred.
        files.is_file(pathname))
示例#4
0
def is_conda() -> bool:
    '''
    ``True`` only if the active Python interpreter is managed by ``conda``, the
    open-source, cross-platform, language-agnostic package manager provided by
    the Anaconda and Miniconda distributions.

    Specifically, this function returns ``True`` only if the
    ``{sys.prefix}/conda-meta/history`` file exists such that:

    * ``{sys.prefix}`` is the site-specific Python directory prefix. Under
      Linux, this is typically:

      * ``/usr`` for system-wide Python interpreters.
      * A user-specific directory for ``conda``-based Python interpreters
        (e.g., ``${HOME}/Miniconda3/envs/${CONDA_ENV_NAME}``).

    * ``conda-meta/history`` is a file relative to this prefix guaranteed to
      exist for *all* ``conda`` environments (including the base environment).
      Since this file is unlikely to be created in non-``conda`` environments,
      the existence of this file effectively guarantees this interpreter to be
      managed by ``conda``.

    See Also
    ----------
    https://stackoverflow.com/a/47730405/2809027
        StackOverflow answer strongly inspiring this implementation.
    https://github.com/conda/constructor/blob/2.0.1/constructor/install.py#L218-L234
        Function in the ``conda`` codebase responsible for creating the
        ``{sys.prefix}/conda-meta/history`` file.
    '''

    # Avoid circular import dependencies.
    from betse.util.path import files, pathnames

    # Absolute filename of a file relative to the site-specific Python
    # directory prefix guaranteed to exist for all "conda" environments.
    conda_history_filename = pathnames.join(sys.prefix, 'conda-meta',
                                            'history')

    # Return true only if this file exists.
    return files.is_file(conda_history_filename)
示例#5
0
def start_code_repl() -> None:
    '''
    Start a REPL based on the canonical :mod:`code` module.
    '''

    # Log this invocation, including the same helpful help line preceding the
    # official Python 3 REPL.
    logs.log_info('Starting "code"-based REPL...')
    logs.log_info('Type "help", "copyright", "credits" or '
                  '"license" for more information.')

    # Defer heavyweight imports.
    import code, readline

    # Absolute path of the file persisting a REPL-specific history of commands.
    # Note this REPL leverages a "readline"-style history file format.
    history_filename = appmetaone.get_app_meta().get_repl_history_filename(
        'readline')
    readline.set_history_length(1000)

    # If this file exists, deserialize this REPL's history from this file.
    if files.is_file(history_filename):
        logs.log_debug('Restoring REPL history from: %s', history_filename)
        readline.read_history_file(history_filename)

    # Run this REPL.
    try:
        code.interact(banner="", local=replenv.REPL_ENV)
    # When this REPL halts...
    except SystemExit as exception:
        # If this REPL halted with error, reraise this exception.
        if cmdexit.is_failure(exception.code):
            raise
        # Else, this REPL halted without error. Silently ignore this exception.
    # Serialize this REPL's history back to disk regardless of whether an
    # exception was raised.
    finally:
        logs.log_debug('Preserving REPL history to: %s', history_filename)
        readline.write_history_file(history_filename)
示例#6
0
    def _do_try(self) -> object:
        '''
        Run the ``try`` subcommand and return the result of doing so.
        '''

        # Basename of the sample configuration file to be created.
        config_basename = 'sample_sim.yaml'

        # Relative filename of this file, relative to the current directory.
        self._args.conf_filename = pathnames.join(
            'sample_sim', config_basename)

        #FIXME: Insufficient. We only want to reuse this file if this file's
        #version is identical to that of the default YAML configuration file's
        #version. Hence, this logic should (arguably) be shifted elsewhere --
        #probably into "betse.science.sim_config".

        # If this file already exists, reuse this file.
        if files.is_file(self._args.conf_filename):
            logs.log_info(
                'Reusing simulation configuration "%s".', config_basename)
        # Else, create this file.
        else:
            self._do_config()

        # Run all general-purposes phases, thus excluding network-isolated
        # phases (e.g., "_do_sim_grn"), in the expected order.
        self._do_seed()
        self._do_init()
        self._do_sim()
        self._do_plot_seed()
        self._do_plot_init()

        # Return the value returned by the last such phase, permitting this
        # subcommand to be memory profiled. While any value would technically
        # suffice, the value returned by the last such phase corresponds to a
        # complete simulation run and hence is likely to consume maximal memory.
        return self._do_plot_sim()
示例#7
0
def _die_unless_file_pickled(
    filename: str, subcommand: str, subcommand_label: str) -> None:
    '''
    Raise an exception containing the passed machine-readable subcommand and
    human-readable label denoting that subcommand unless the file with the
    passed filename pickled by a prior run of that subcommand exists.

    Parameters
    ----------
    filename : str
        Absolute filename of the pickled file to be validated.
    subcommand : str
        Machine-readable case-sensitive name of the prior subcommand expected
        to have pickled this file (e.g., ``seed``, ``init``).
    subcommand_label : str
        Human-readable case-insensitive noun denoting the type of prior
        subcommand expected to have pickled this file (e.g., ``Seed``,
        ``initialization``).

    Raises
    ----------
    BetseSimException
        If this file does *not* exist.
    '''

    # If this file does *NOT* exist...
    if not files.is_file(filename):
        # Uppercase the first character of this label for readability.
        subcommand_label_cased = strs.uppercase_char_first(subcommand_label)

        # Raise an exception embedding these parameters.
        raise BetseSimException(
            '{} not previously run; '
            'please run "betse {}" and try again.\n'
            'Specifically, file not found: {}'.format(
                subcommand_label_cased, subcommand, filename))
示例#8
0
    def _run_pyinstaller_command(
        self,
        script_basename: str,
        script_type: str,
        entry_point,
    ) -> None:
        '''
        Run the currently configured PyInstaller command for the passed entry
        point's script wrapper.

        Attributes
        ----------
        script_basename : str
            Basename of the executable wrapper script running this entry point.
        script_type : str
            Type of the executable wrapper script running this entry point,
            guaranteed to be either:

            * If this script is console-specific, ``console`` .
            * Else, ``gui``.
        entry_point : EntryPoint
            Entry point, whose attributes specify the module to be imported and
            function to be run by this script.
        '''

        # Defer heavyweight imports.
        from betse.util.io import stderrs
        from betse.util.os.shell import shellstr
        from betse.util.path import files, pathnames

        # If this spec exists, instruct PyInstaller to reuse rather than
        # recreate this file, thus preserving edits to this file.
        if files.is_file(self._pyinstaller_spec_filename):
            print('Reusing spec file "{}".'.format(
                self._pyinstaller_spec_filename))

            # Append the relative path of this spec file.
            self._pyinstaller_args.append(
                shellstr.shell_quote(self._pyinstaller_spec_filename))

            # Freeze this script with this spec file.
            self._run_pyinstaller_imported()
        # Else, instruct PyInstaller to (re)create this spec file.
        else:
            # Absolute path of the directory containing this files.
            pyinstaller_spec_dirname = pathnames.get_dirname(
                self._pyinstaller_spec_filename)

            # Absolute path of the current script wrapper.
            script_filename = pathnames.join(self.install_dir, script_basename)
            files.die_unless_file(
                script_filename,
                'File "{}" not found. {}'.format(script_filename,
                                                 freeze.EXCEPTION_ADVICE))

            # Inform the user of this action *AFTER* the above validation.
            # Since specification files should typically be reused rather
            # than regenerated, do so as a non-fatal warning.
            stderrs.output_warning('Generating spec file "{}".'.format(
                self._pyinstaller_spec_filename))

            # Append all options specific to spec file generation.
            self._pyinstaller_args.extend([
                # If this is a console script, configure standard input and
                # output for console handling; else, do *NOT* and, if the
                # current operating system is OS X, generate an ".app"-suffixed
                # application bundle rather than a customary executable.
                '--console' if script_type == 'console' else '--windowed',

                # Non-default PyInstaller directories.
                '--additional-hooks-dir=' +
                shellstr.shell_quote(self._pyinstaller_hooks_dirname),
                '--specpath=' + shellstr.shell_quote(pyinstaller_spec_dirname),
            ])

            # Append all subclass-specific options.
            self._pyinstaller_args.extend(self._get_pyinstaller_options())

            # Append the absolute path of this script.
            self._pyinstaller_args.append(
                shellstr.shell_quote(script_filename))

            # Freeze this script and generate a spec file.
            self._run_pyinstaller_imported()

            # Absolute path of this file.
            script_spec_filename = pathnames.join(pyinstaller_spec_dirname,
                                                  script_basename + '.spec')

            # Rename this file to have the basename expected by the prior
            # conditional on the next invocation of this setuptools command.
            #
            # Note that "pyinstaller" accepts an option "--name" permitting
            # the basename of this file to be specified prior to generating
            # this file. Unfortunately, this option *ALSO* specifies the
            # basename of the generated executable. While the former is
            # reliably renamable, the former is *NOT* (e.g., due to code
            # signing). Hence, this file is manually renamed without passing
            # this option.
            files.move_file(script_spec_filename,
                            self._pyinstaller_spec_filename)
示例#9
0
    def sim_grn(self) -> SimPhase:
        '''
        Initialize and simulate a pure gene regulatory network (GRN) *without*
        bioelectrics with the cell cluster seeded by a prior call to the
        :meth:`seed` method and cache this initialization and simulation to
        output files, specified by the current configuration file.

        This method *must* be called prior to the :meth:`plot_grn` method, which
        consumes this output as input.

        Returns
        ----------
        SimPhase
            High-level simulation phase instance encapsulating all objects
            internally created by this method to run this phase.
        '''

        # Simulation phase objects, defaulting to undefined initially.
        phase = None

        # Simulation phase type.
        phase_kind = SimPhaseKind.INIT

        # Simulator objects initialized below.
        cells = None
        sim = None

        # Log this simulation.
        logs.log_info(
            'Running gene regulatory network "%s" '
            'defined in config file "%s"...',
            pathnames.get_basename(self._p.grn_config_filename),
            self._p.conf_basename)

        # If networking an uninitialized, unsimulated cell cluster...
        if self._p.grn_unpickle_phase_type is GrnUnpicklePhaseType.SEED:
            if not files.is_file(self._p.seed_pickle_filename):
                if not self._p.autoInit:
                    raise BetseSimException(
                        'Simulation halted due to missing core seed. '
                        'Please run "betse seed" and try again.')

                # Create an instance of world.
                logs.log_info('Automatically seeding cell cluster...')
                self.seed()

            # Load the seed from cache.
            cells, _ = fh.loadWorld(self._p.seed_pickle_filename)
            logs.log_info('Running gene regulatory network on betse seed...')
            logs.log_info('Now using cell cluster to run initialization.')

            # Simulation phase.
            phase = SimPhase(
                kind=phase_kind,
                callbacks=self._callbacks,
                cells=cells,
                p=self._p,
            )

            # Initialize core simulation data structures.
            phase.sim.init_core(phase)
            phase.sim.init_dynamics(phase)

            #FIXME: Shift the following assignments into a new public
            #"Simulator" method -- say, Simulator.init_core_null().

            # Initialize other aspects required for piggyback of GRN on the
            # sim object.
            phase.sim.time = []
            phase.sim.vm = -50e-3 * np.ones(phase.sim.mdl)

            # Initialize key fields of simulator required to interface
            # (dummy init).
            phase.sim.rho_pump = 1.0
            phase.sim.rho_channel = 1.0
            phase.sim.conc_J_x = np.zeros(phase.sim.edl)
            phase.sim.conc_J_y = np.zeros(phase.sim.edl)
            phase.sim.J_env_x  = np.zeros(phase.sim.edl)
            phase.sim.J_env_y  = np.zeros(phase.sim.edl)
            phase.sim.u_env_x  = np.zeros(phase.sim.edl)
            phase.sim.u_env_y  = np.zeros(phase.sim.edl)
        # Else if networking an initialized but unsimulated cell cluster...
        elif self._p.grn_unpickle_phase_type is GrnUnpicklePhaseType.INIT:
            if not files.is_file(self._p.init_pickle_filename):
                if not self._p.autoInit:
                    raise BetseSimException(
                        'Simulation halted due to missing core initialization. '
                        'Please run "betse init" and try again.')

                logs.log_info('Automatically initializing cell cluster...')
                self.init()
                logs.log_info('Now using initialization to run simulation.')

            # Load the initialization from cache.
            logs.log_info('Running gene regulatory network on betse init...')
            sim, cells, _ = fh.loadSim(self._p.init_pickle_filename)
        # Else if networking an initialized, simulated cell cluster...
        elif self._p.grn_unpickle_phase_type is GrnUnpicklePhaseType.SIM:
            if not files.is_file(self._p.sim_pickle_filename):
                raise BetseSimException(
                    'Simulation halted due to missing core simulation. '
                    'Please run "betse sim" and try again.')

            # Load the simulation from cache.
            logs.log_info('Running gene regulatory network on betse sim...')
            sim, cells, _ = fh.loadSim(self._p.sim_pickle_filename)
        # Else, this type of networking is unrecognized. Raise an exception.
        else:
            raise BetseSimConfException(
                'Gene regulatory network (GRN) unpickle simulation phase '
                '"{}" unrecognized.'.format(self._p.grn_unpickle_phase_type))

        # If *NOT* defined above, define this simulation phase.
        if phase is None:
            phase = SimPhase(
                kind=phase_kind,
                callbacks=self._callbacks,
                cells=cells,
                p=self._p,
                sim=sim,
            )

            # Reinitialize all profiles.
            phase.dyna.init_profiles(phase)
            phase.dyna.init_events(phase)

        # If *NOT* restarting from a prior GRN run, start a new GRN.
        if self._p.grn_unpickle_filename is None:
            # Log this start.
            logs.log_info("Initializing the gene regulatory network...")

            # Create and initialize an instance of master of metabolism.
            MoG = MasterOfGenes(self._p)
            MoG.read_gene_config(phase)
        # Else, restart from a prior GRN run.
        else:
            # Log this restart.
            logs.log_info(
                'Reinitializing the gene regulatory network from "%s"...',
                pathnames.get_basename(self._p.grn_unpickle_filename))

            # If this file does *NOT* exist, raise an exception.
            _die_unless_file_pickled(
                filename=self._p.grn_unpickle_filename,
                subcommand='sim-grn',
                subcommand_label='Gene regulatory network')

            # Unpickle this file into a high-level "MasterOfGenes" object.
            MoG, _, _ = pickles.load(self._p.grn_unpickle_filename)

            # If running on a sim with a cut event, perform this cut...
            if (
                phase.dyna.event_cut is not None and
                phase.dyna.event_cut.is_fired
            ):
                # Log this cutting.
                logs.log_info(
                    'A cutting event has been run, '
                    'so the GRN object needs to be modified...')

                # If no prior initialization exists, raise an exception.
                if not files.is_file(self._p.sim_pickle_filename):
                    logs.log_warning(
                        'This situation is complex '
                        'due to a cutting event being run. '
                        'Please have a corresponding init file '
                        'to run the GRN simulation!')

                    raise BetseSimException(
                        'Simulation terminated due to missing core init. '
                        'Please alter GRN settings and try again.')
                # Else, a prior initialization exists.

                # Log this initialization.
                logs.log_info(
                    'Loading betse init from cache '
                    'for reference to original cells...')

                # Load the initialization from cache.
                sim_old, cells_old, _ = fh.loadSim(self._p.sim_pickle_filename)

                #FIXME: This phase object would ideally be pickled to and
                #from the "self._p.sim_pickle_filename" file loaded above, in
                #which case this local variable would be safely removable.

                # Original simulation phase. To avoid caller confusion, the
                # optional "callbacks" parameter is intentionally *NOT* passed.
                phase_old = SimPhase(
                    kind=phase_kind,
                    p=self._p,
                    cells=cells_old,
                    sim=sim_old,
                )

                # Initialize all tissue profiles on original cells.
                phase_old.dyna.init_profiles(phase_old)

                for cut_profile_name in phase_old.p.event_cut_profile_names:
                    logs.log_info(
                        'Cutting cell cluster via cut profile "%s"...',
                        cut_profile_name)

                    # Object picking the cells removed by this cut profile.
                    tissue_picker = phase_old.dyna.cut_name_to_profile[
                        cut_profile_name].picker

                    # One-dimensional Numpy arrays of the indices of all
                    # cells and cell membranes to be removed.
                    target_inds_cell, target_inds_mems = (
                        tissue_picker.pick_cells_and_mems(
                            cells=cells_old, p=self._p))

                    MoG.core.mod_after_cut_event(
                        phase, target_inds_cell, target_inds_mems)
                    MoG.core.redefine_dynamic_dics(sim, cells, self._p)

                    logs.log_info(
                        'Reinitializing gene regulatory network '
                        'for simulation...')
                    MoG.reinitialize(phase)

            # if self._p.use_microtubules:
            #     sim.mtubes.mtubes_x = MoG.mtubes_x_time[-1]
            #     sim.mtubes.mtubes_y = MoG.mtubes_y_time[-1]
            #
            #     sim.mtubes.uxmt, sim.mtubes.uymt = sim.mtubes.mtubes_to_cell(
            #         cells, self._p)

        logs.log_info('Simulating gene regulatory network...')
        MoG.run_core_sim(phase)

        # Return this phase.
        return phase
示例#10
0
def get_package_project_dirname_or_none(package: ModuleType) -> StrOrNoneTypes:
    '''
    **Absolute canonical dirname** (i.e., absolute dirname after resolving
    symbolic links) of the **root project directory** (i.e., top-level
    directory containing an installable ``pyproject.toml`` file or ``setup.py``
    script) governing the passed top-level Python package if found *or* raise
    an exception otherwise (i.e., if this directory is *not* found).

    Equivalently, this is the same as both:

    * The root directory archived by release tarballs for this application.
    * The Git-based working tree for this application (i.e., the top-level
      directory containing this application's ``.git`` subdirectory).

    Specifically, this function returns non-``None`` only if the parent
    directory of the passed package's directory contains either:

    * A ``pyproject.toml`` file, implying this package to be satisfy `PEP 518`_
      and hence be installable by at least poetry_.
    * A ``setup.py`` script, implying this package to be installable by either
      the standard :mod:`distutils` API or third-party :mod:`setuptools` API.

    :: _PEP 518:
       https://snarky.ca/clarifying-pep-518/
    :: _poetry:
       https://github.com/sdispater/poetry

    Caveats
    ----------
    **This directory typically does not exist.** This directory is only
    required during installation by non-developers *or* during development
    by developers. Once this application has been installed in a standard
    (i.e., non-editable) fashion by non-developers, this directory is no
    longer required and hence should *not* be assumed to exist.

    Parameters
    ----------
    package : ModuleType
        Top-level Python package to be queried.

    Returns
    ----------
    StrOrNoneTypes
        Either:

        * If the parent directory of the passed package's directory contains
          either a ``pyproject.toml`` or ``setup.py`` script, the absolute
          canonical dirname of that parent directory.
        * Else, ``None``.
    '''

    # Avoid circular import dependencies.
    from betse.util.path import files, pathnames
    from betse.util.py.module import pymodule

    # Absolute canonical dirname of the directory providing this package,
    # canonicalized into a directory rather than symbolic link to increase the
    # likelihood of obtaining the actual parent directory of this package.
    package_dirname = pymodule.get_dirname_canonical(package)

    # Absolute dirname of the parent directory of this directory.
    package_project_dirname = pathnames.get_dirname(package_dirname)

    # Absolute filenames of the "pyproject.toml" and "setup.py" files possibly
    # provided by this parent directory.
    pyproject_filename = pathnames.join(package_project_dirname,
                                        'pyproject.toml')
    setup_filename = pathnames.join(package_project_dirname, 'setup.py')

    # Return this parent directory's absolute dirname if either of these
    # requisite installation-time files exist *OR* "None" otherwise.
    return (package_project_dirname if
            (files.is_file(pyproject_filename)
             or files.is_file(setup_filename)) else None)