Ejemplo n.º 1
0
    def get_dynamic_component(model_type, id):
        """ Get a simulation's dynamic component

        Args:
            model_type (:obj:`type`): the subclass of `DynamicComponent` (or `obj_tables.Model`) being retrieved
            id (:obj:`str`): the dynamic component's id

        Returns:
            :obj:`DynamicComponent`: the dynamic component

        Raises:
            :obj:`MultialgorithmError`: if the dynamic component cannot be found
        """
        if not inspect.isclass(model_type) or not issubclass(
                model_type, DynamicComponent):
            model_type = DynamicComponent.get_dynamic_model_type(model_type)
        if model_type not in DynamicComponent.dynamic_components_objs:
            raise MultialgorithmError(
                "model type '{}' not in DynamicComponent.dynamic_components_objs"
                .format(model_type.__name__))
        if id not in DynamicComponent.dynamic_components_objs[model_type]:
            raise MultialgorithmError(
                "model type '{}' with id='{}' not in DynamicComponent.dynamic_components_objs"
                .format(model_type.__name__, id))
        return DynamicComponent.dynamic_components_objs[model_type][id]
Ejemplo n.º 2
0
    def validate(self):
        """ Fully validate a `WCSimulationConfig` instance

        Returns:
            :obj:`None`: if no error is found

        Raises:
            :obj:`MultialgorithmError`: if validation fails
        """

        self.validate_individual_fields()

        de_sim_config = self.de_simulation_config

        # Max time must be positive
        if de_sim_config.time_max <= 0:
            raise MultialgorithmError(
                "Maximum time ({de_sim_config.time_max}) must be positive")

        if de_sim_config.output_dir is None and self.checkpoint_period is not None:
            raise MultialgorithmError(
                f"a data directory (self.de_simulation_config.output_dir) must be "
                f"provided when a checkpoint_period ({self.checkpoint_period}) is provided"
            )

        # Check that timesteps divide evenly into the simulation duration
        self.check_periodic_timestep('ode_time_step')
        self.check_periodic_timestep('dfba_time_step')
        self.check_periodic_timestep('checkpoint_period')

        # verbose and profile should not both be set
        if self.verbose and de_sim_config.profile:
            raise MultialgorithmError(
                f"verbose and profile cannot both be true")
Ejemplo n.º 3
0
    def __init__(self,
                 dynamic_model,
                 random_state,
                 wc_lang_compartment,
                 species_ids=None):
        """ Initialize the volume and density of this :obj:`DynamicCompartment`\ .

        Args:
            dynamic_model (:obj:`DynamicModel`): the simulation's dynamic model
            random_state (:obj:`numpy.random.RandomState`): a random state
            wc_lang_compartment (:obj:`Compartment`): the corresponding static `wc_lang` `Compartment`
            species_ids (:obj:`list` of :obj:`str`, optional): the IDs of the species stored
                in this compartment

        Raises:
            :obj:`MultialgorithmError`: if `self.init_volume` or `self.init_density` are not
                positive numbers
        """
        super(DynamicCompartment, self).__init__(dynamic_model, None,
                                                 wc_lang_compartment)

        self.id = wc_lang_compartment.id
        self.biological_type = wc_lang_compartment.biological_type
        self.physical_type = wc_lang_compartment.physical_type
        self.species_ids = species_ids

        # obtain initial compartment volume by sampling its specified distribution
        if wc_lang_compartment.init_volume and \
            are_terms_equivalent(wc_lang_compartment.init_volume.distribution, onto['WC:normal_distribution']):
            mean = wc_lang_compartment.init_volume.mean
            std = wc_lang_compartment.init_volume.std
            if numpy.isnan(std):
                std = mean / self.MEAN_TO_STD_DEV_RATIO
            self.init_volume = max(0., random_state.normal(mean, std))
        else:
            raise MultialgorithmError(
                'Initial volume must be normally distributed')

        if math.isnan(self.init_volume):  # pragma no cover: cannot be True
            raise MultialgorithmError(
                "DynamicCompartment {}: init_volume is NaN, but must be a positive "
                "number.".format(self.id))
        if self.init_volume <= 0:
            raise MultialgorithmError(
                "DynamicCompartment {}: init_volume ({}) must be a positive "
                "number.".format(self.id, self.init_volume))

        if not self._is_abstract():
            init_density = wc_lang_compartment.init_density.value
            if math.isnan(init_density):
                raise MultialgorithmError(
                    f"DynamicCompartment {self.id}: init_density is NaN, but must "
                    f"be a positive number.")
            if init_density <= 0:
                raise MultialgorithmError(
                    f"DynamicCompartment {self.id}: init_density ({init_density}) "
                    f"must be a positive number.")
            self.init_density = init_density
Ejemplo n.º 4
0
    def __init__(self, model, wc_sim_config, options=None):
        """
        Args:
            model (:obj:`Model`): the model being simulated
            wc_sim_config (:obj:`WCSimulationConfig`): a whole-cell simulation configuration
            options (:obj:`dict`, optional): options for submodels, keyed by submodel class name
        """
        # initialize simulation infrastructure
        self.simulation = SimulationEngine()
        self.random_state = RandomStateManager.instance()

        # create simulation attributes
        self.model = model
        self.wc_sim_config = wc_sim_config
        self.options = options

        self._skipped_submodels = self.prepare_skipped_submodels()

        # a model without submodels cannot be simulated
        submodel_ids = set(
            [submodel.id for submodel in self.model.get_submodels()])
        if not submodel_ids - self.skipped_submodels():
            raise MultialgorithmError(
                f"model {self.model.id} cannot be simulated because it contains"
                f" no submodels")
Ejemplo n.º 5
0
    def eval(self, time):
        """ Evaluate this mathematical expression

        Approach:
            * Replace references to related Models in `self.wc_sim_tokens` with their values
            * Join the elements of `self.wc_sim_tokens` into a Python expression
            * `eval` the Python expression

        Args:
            time (:obj:`float`): the simulation time at which the expression should be evaluated

        Returns:
            :obj:`float`: the value of this :obj:`DynamicExpression` at time `time`

        Raises:
            :obj:`MultialgorithmError`: if Python `eval` raises an exception
        """
        assert hasattr(
            self,
            'wc_sim_tokens'), "'{}' must use prepare() before eval()".format(
                self.id)
        for idx, sim_token in enumerate(self.wc_sim_tokens):
            if sim_token.code == SimTokCodes.dynamic_expression:
                self.expr_substrings[idx] = str(
                    sim_token.dynamic_expression.eval(time))
        try:
            return eval(''.join(self.expr_substrings), {}, self.local_ns)
        except BaseException as e:
            raise MultialgorithmError("eval of '{}' raises {}: {}'".format(
                self.expression,
                type(e).__name__, str(e)))
Ejemplo n.º 6
0
    def __init__(self, dynamic_model, local_species_population, wc_lang_model,
                 wc_lang_expression):
        """
        Args:
            dynamic_model (:obj:`DynamicModel`): the simulation's dynamic model
            local_species_population (:obj:`LocalSpeciesPopulation`): the simulation's species population store
            wc_lang_model (:obj:`obj_tables.Model`): the corresponding `wc_lang` `Model`
            wc_lang_expression (:obj:`ParsedExpression`): an analyzed and validated expression

        Raises:
            :obj:`MultialgorithmError`: if `wc_lang_expression` does not contain an analyzed,
                validated expression
        """

        super().__init__(dynamic_model, local_species_population,
                         wc_lang_model)

        # wc_lang_expression must have been successfully `tokenize`d.
        if not wc_lang_expression._obj_tables_tokens:
            raise MultialgorithmError(
                "_obj_tables_tokens cannot be empty - ensure that '{}' is valid"
                .format(wc_lang_model))
        # optimization: self.wc_lang_expression will be deleted by prepare()
        self.wc_lang_expression = wc_lang_expression
        self.expression = wc_lang_expression.expression
Ejemplo n.º 7
0
    def test_errors(self):
        msg = 'test msg'

        with self.assertRaisesRegexp(Error, msg):
            raise Error(msg)

        with self.assertRaisesRegexp(MultialgorithmError, msg):
            raise MultialgorithmError(msg)

        with self.assertRaisesRegexp(SpeciesPopulationError, msg):
            raise SpeciesPopulationError(msg)

        def expected(time, msg):
            return f"{time}: {msg}"

        time = 3.5
        with self.assertRaisesRegexp(DynamicFrozenSimulationError,
                                     expected(time, msg)):
            raise DynamicFrozenSimulationError(time, msg)

        with self.assertRaisesRegexp(DynamicMultialgorithmError,
                                     expected(time, msg)):
            raise DynamicMultialgorithmError(time, msg)

        with self.assertRaisesRegexp(DynamicSpeciesPopulationError,
                                     expected(time, msg)):
            raise DynamicSpeciesPopulationError(time, msg)
Ejemplo n.º 8
0
 def _prepare(self):
     """ Prepare and validate the model, and create simulation metadata
     """
     # prepare & check the model
     PrepForWcSimTransform().run(self.model)
     errors = Validator().run(self.model)
     if errors:
         raise MultialgorithmError(
             indent_forest(['The model is invalid:', [errors]]))
Ejemplo n.º 9
0
    def __init__(self, results_dir):
        """ Create a `RunResults`

        Args:
            results_dir (:obj:`str`): directory storing checkpoints and/or HDF5 file with
                the simulation run results
        """
        if results_dir is None:
            raise MultialgorithmError('results_dir is None')
        if not os.path.isdir(results_dir):
            raise MultialgorithmError(
                f'results_dir {results_dir} must be a directory')
        self.results_dir = results_dir
        self.run_results = {}

        # if an HDF file containing the run results does not exist, then
        # create it from the stored metadata and sequence of checkpoints
        if not os.path.isfile(self._hdf_file()):

            # create the HDF file containing the run results
            population_df, observables_df, functions_df, aggregate_states_df, random_states_s = \
                self.convert_checkpoints()

            # populations
            population_df.to_hdf(self._hdf_file(), 'populations')

            # observables
            observables_df.to_hdf(self._hdf_file(), 'observables')

            # functions
            functions_df.to_hdf(self._hdf_file(), 'functions')

            # aggregate states
            aggregate_states_df.to_hdf(self._hdf_file(), 'aggregate_states')

            # random states
            random_states_s.to_hdf(self._hdf_file(), 'random_states')

            # metadata
            self.convert_metadata(SimulationMetadata)
            self.convert_metadata(WCSimulationMetadata)

        # load the data in the HDF file containing the run results
        self._load_hdf_file()
Ejemplo n.º 10
0
    def get_dynamic_model_type(model_type):
        """ Get a simulation's dynamic component type

        Obtain a dynamic component type from a corresponding `wc_lang` Model type, instance or
        string name.

        Args:
            model_type (:obj:`Object`): a `wc_lang` Model type represented by a subclass of `obj_tables.Model`,
                an instance of `obj_tables.Model`, or a string name for a `obj_tables.Model`

        Returns:
            :obj:`type`: the dynamic component

        Raises:
            :obj:`MultialgorithmError`: if the corresponding dynamic component type cannot be determined
        """
        if isinstance(model_type, type) and issubclass(model_type,
                                                       obj_tables.Model):
            if model_type in WC_LANG_MODEL_TO_DYNAMIC_MODEL:
                return WC_LANG_MODEL_TO_DYNAMIC_MODEL[model_type]
            raise MultialgorithmError(
                "model class of type '{}' not found".format(
                    model_type.__name__))

        if isinstance(model_type, obj_tables.Model):
            if model_type.__class__ in WC_LANG_MODEL_TO_DYNAMIC_MODEL:
                return WC_LANG_MODEL_TO_DYNAMIC_MODEL[model_type.__class__]
            raise MultialgorithmError("model of type '{}' not found".format(
                model_type.__class__.__name__))

        if isinstance(model_type, str):
            model_type_type = getattr(wc_lang, model_type, None)
            if model_type_type is not None:
                if model_type_type in WC_LANG_MODEL_TO_DYNAMIC_MODEL:
                    return WC_LANG_MODEL_TO_DYNAMIC_MODEL[model_type_type]
                raise MultialgorithmError(
                    "model of type '{}' not found".format(
                        model_type_type.__name__))
            raise MultialgorithmError(
                "model type '{}' not defined".format(model_type))

        raise MultialgorithmError(
            "model type '{}' has wrong type".format(model_type))
Ejemplo n.º 11
0
    def _check_component(self, component):
        """ Raise an exception if the component is empty

        Args:
            component (:obj:`str`): the name of the component to check

        Raises:
            :obj:`MultialgorithmError`: if `component` is empty
        """
        data = self.get(component)
        if data.empty:
            raise MultialgorithmError(f"'{component}' component is empty")
Ejemplo n.º 12
0
    def initialize_mass_and_density(self, species_population):
        """ Initialize the species populations and the mass accounted for by species.

        Also initialize the fraction of density accounted for by species, `self.accounted_fraction`.

        Args:
            species_population (:obj:`LocalSpeciesPopulation`): the simulation's species population store

        Raises:
            :obj:`MultialgorithmError`: if `accounted_fraction == 0` or
                if `self.MAX_ALLOWED_INIT_ACCOUNTED_FRACTION < accounted_fraction`
        """
        self.species_population = species_population
        self.init_accounted_mass = self.accounted_mass(time=0)
        if self._is_abstract():
            self.init_mass = self.init_accounted_mass
        else:
            self.init_mass = self.init_density * self.init_volume
            self.init_accounted_density = self.init_accounted_mass / self.init_volume
            # calculate fraction of initial mass or density represented by species
            self.accounted_fraction = self.init_accounted_density / self.init_density
            # also, accounted_fraction = self.init_accounted_mass / self.init_mass

            # usually epsilon < accounted_fraction <= 1, where epsilon depends on how thoroughly
            # processes in the compartment are characterized
            if 0 == self.accounted_fraction:
                raise MultialgorithmError(
                    "DynamicCompartment '{}': initial accounted ratio is 0".
                    format(self.id))
            elif 1.0 < self.accounted_fraction <= self.MAX_ALLOWED_INIT_ACCOUNTED_FRACTION:
                warnings.warn(
                    "DynamicCompartment '{}': initial accounted ratio ({:.3E}) greater than 1.0"
                    .format(self.id,
                            self.accounted_fraction), MultialgorithmWarning)
            if self.MAX_ALLOWED_INIT_ACCOUNTED_FRACTION < self.accounted_fraction:
                raise MultialgorithmError(
                    "DynamicCompartment {}: initial accounted ratio ({:.3E}) greater "
                    "than self.MAX_ALLOWED_INIT_ACCOUNTED_FRACTION ({}).".
                    format(self.id, self.accounted_fraction,
                           self.MAX_ALLOWED_INIT_ACCOUNTED_FRACTION))
Ejemplo n.º 13
0
    def __init__(self,
                 id,
                 dynamic_model,
                 reactions,
                 species,
                 dynamic_compartments,
                 local_species_population,
                 default_center_of_mass=None,
                 options=None):
        """ Initialize an SSA submodel object.

        Args:
            id (:obj:`str`): unique id of this dynamic SSA submodel
            dynamic_model (:obj:`DynamicModel`): the aggregate state of a simulation
            reactions (:obj:`list` of :obj:`Reaction`): the reactions modeled by this SSA submodel
            species (:obj:`list` of :obj:`Species`): the species that participate in the reactions modeled
                by this SSA submodel, with their initial concentrations
            dynamic_compartments (:obj:`dict`): :obj:`DynamicCompartment`\ s, keyed by id, that contain
                species which participate in reactions that this SSA submodel models, including
                adjacent compartments used by its transfer reactions
            local_species_population (:obj:`LocalSpeciesPopulation`): the store that maintains this
                SSA submodel's species population
            default_center_of_mass (:obj:`float`, optional): the center-of-mass for the
                :obj:`ExponentialMovingAverage`
            options (:obj:`dict`, optional): SSA submodel options

        Raises:
            :obj:`MultialgorithmError`: if the initial SSA wait exponential moving average is not positive
        """
        super().__init__(id, dynamic_model, reactions, species,
                         dynamic_compartments, local_species_population)
        self.options = options
        self.num_SsaWaits = 0
        # `initial_ssa_wait_ema` must be positive, as otherwise an infinite sequence of SsaWait
        # messages will be executed at the start of a simulation if no reactions are enabled
        initial_ssa_wait_ema = config_multialgorithm['initial_ssa_wait_ema']
        if initial_ssa_wait_ema <= 0:  # pragma: no cover
            raise MultialgorithmError(
                f"'initial_ssa_wait_ema' must be positive to avoid infinite sequence of "
                "SsaWait messages, but it is {initial_ssa_wait_ema}")
        if default_center_of_mass is None:
            default_center_of_mass = config_multialgorithm[
                'default_center_of_mass']
        self.ema_of_inter_event_time = ExponentialMovingAverage(
            initial_ssa_wait_ema, center_of_mass=default_center_of_mass)
        self.random_state = RandomStateManager.instance()

        self.log_with_time("init: id: {}".format(id))
        self.log_with_time("init: species: {}".format(
            str([s.id for s in species])))
Ejemplo n.º 14
0
    def validate_individual_fields(self):
        """ Validate individual fields in a `WCSimulationConfig` instance

        Returns:
            :obj:`None`: if no error is found

        Raises:
            :obj:`MultialgorithmError`: if an attribute fails validation
        """

        # additional validation
        if self.ode_time_step is not None and self.ode_time_step <= 0:
            raise MultialgorithmError(
                f'ode_time_step ({self.ode_time_step}) must be positive')

        if self.dfba_time_step is not None and self.dfba_time_step <= 0:
            raise MultialgorithmError(
                f'dfba_time_step ({self.dfba_time_step}) must be positive')

        if self.checkpoint_period is not None and self.checkpoint_period <= 0:
            raise MultialgorithmError(
                f'checkpoint_period ({self.checkpoint_period}) must be positive'
            )
Ejemplo n.º 15
0
    def _prepare_computed_components(cls):
        """ Check and initialize the `COMPUTED_COMPONENTS`

        Raises:
            :obj:`MultialgorithmError`: if a value in `self.COMPUTED_COMPONENTS` is not a method
                in `RunResults`
        """
        for component, method in cls.COMPUTED_COMPONENTS.items():
            if hasattr(cls, method):
                cls.COMPUTED_COMPONENTS[component] = getattr(cls, method)
            else:
                raise MultialgorithmError(
                    "'{}' in COMPUTED_COMPONENTS is not a method in {}".format(
                        method, cls.__name__))
Ejemplo n.º 16
0
    def __init__(self,
                 id,
                 dynamic_model,
                 reactions,
                 species,
                 dynamic_compartments,
                 local_species_population,
                 ode_time_step,
                 options=None):
        """ Initialize an ODE submodel instance

        Args:
            id (:obj:`str`): unique id of this dynamic ODE submodel
            dynamic_model (:obj: `DynamicModel`): the aggregate state of a simulation
            reactions (:obj:`list` of `wc_lang.Reaction`): the reactions modeled by this ODE submodel
            species (:obj:`list` of `wc_lang.Species`): the species that participate in the reactions
                modeled by this ODE submodel
            dynamic_compartments (:obj: `dict`): `DynamicCompartment`s, keyed by id, that contain
                species which participate in reactions that this ODE submodel models, including
                adjacent compartments used by its transfer reactions
            local_species_population (:obj:`LocalSpeciesPopulation`): the store that maintains this
                ODE submodel's species population
            ode_time_step (:obj:`float`): time interval between ODE analyses
            num_steps (:obj:`int`): number of steps taken
            options (:obj:`dict`, optional): ODE submodel options
        """
        super().__init__(id, dynamic_model, reactions, species,
                         dynamic_compartments, local_species_population)
        if ode_time_step <= 0:
            raise MultialgorithmError(
                f"OdeSubmodel {self.id}: ode_time_step must be positive, but is "
                f"{ode_time_step}")
        self.ode_time_step = ode_time_step
        self.num_steps = 0
        self.set_up_ode_submodel()
        self.set_up_optimizations()
        ode_solver_options = {
            'atol': self.ABS_ODE_SOLVER_TOLERANCE,
            'rtol': self.REL_ODE_SOLVER_TOLERANCE
        }
        if options is not None and 'tolerances' in options:
            if 'atol' in options['tolerances']:
                ode_solver_options['atol'] = options['tolerances']['atol']
            if 'rtol' in options['tolerances']:
                ode_solver_options['rtol'] = options['tolerances']['rtol']
        self.solver = self.create_ode_solver(**ode_solver_options)
Ejemplo n.º 17
0
    def prepare_skipped_submodels(self):
        """ Prepare the IDs of the submodels that will not be run

        Returns:
            :obj:`set` of :obj:`str`: the IDs of submodels that will not run
        """
        if self.wc_sim_config.submodels_to_skip:
            submodels_to_skip = set(self.wc_sim_config.submodels_to_skip)
            submodel_ids = set(
                [submodel.id for submodel in self.model.get_submodels()])
            if submodels_to_skip - submodel_ids:
                raise MultialgorithmError(
                    f"'submodels_to_skip' contains submodels that aren't in the model: "
                    f"{submodels_to_skip - submodel_ids}")
            return submodels_to_skip
        else:
            return set()
Ejemplo n.º 18
0
    def __init__(self, model):
        """
        Args:
            model (:obj:`str` or `Model`): either a path to file(s) describing a `wc_lang` model, or
                a `Model` instance

        Raises:
            :obj:`MultialgorithmError`: if `model` is invalid
        """
        if isinstance(model, Model):
            self.model_path = None
            self.model = model
        elif isinstance(model, str):
            # read model
            self.model_path = os.path.abspath(os.path.expanduser(model))
            self.model = Reader().run(self.model_path)[Model][0]
        else:
            raise MultialgorithmError(
                "model must be a `wc_lang Model` or a pathname for a model, "
                "but its type is {}".format(type(model)))
Ejemplo n.º 19
0
    def check_periodic_timestep(self, periodic_attr):
        """ Check that simulation duration is an integral multiple of a periodic activity's timestep

        Args:
            periodic_attr (:obj:`str`): name of an attribute storing the duration of a periodic activity

        Returns:
            :obj:`None`: if no error is found

        Raises:
            :obj:`MultialgorithmError`: if the simulation duration is not an integral multiple of the
                periodic activity's timestep
        """
        de_sim_cfg = self.de_simulation_config
        if getattr(self, periodic_attr) is not None and \
            (de_sim_cfg.time_max - de_sim_cfg.time_init) / getattr(self, periodic_attr) % 1 != 0:
            raise MultialgorithmError(
                f'(time_max - time_init) ({de_sim_cfg.time_max} - {de_sim_cfg.time_init}) '
                f'must be a multiple of {periodic_attr} ({getattr(self, periodic_attr)})'
            )
Ejemplo n.º 20
0
    def __init__(self, id, dynamic_model, reactions, species, dynamic_compartments,
                 local_species_population, time_step, options=None):
        """ Initialize a dFBA submodel instance

        Args:
            id (:obj:`str`): unique id of this dynamic dFBA submodel
            dynamic_model (:obj: `DynamicModel`): the aggregate state of a simulation
            reactions (:obj:`list` of `wc_lang.Reaction`): the reactions modeled by this dFBA submodel
            species (:obj:`list` of `wc_lang.Species`): the species that participate in the reactions
                modeled by this dFBA submodel
            dynamic_compartments (:obj: `dict`): `DynamicCompartment`s, keyed by id, that contain
                species which participate in reactions that this dFBA submodel models, including
                adjacent compartments used by its transfer reactions
            local_species_population (:obj:`LocalSpeciesPopulation`): the store that maintains this
                dFBA submodel's species population
            time_step (:obj:`float`): time between FBA executions
            options (:obj:`dict`, optional): dFBA submodel options
        """
        super().__init__(id, dynamic_model, reactions, species, dynamic_compartments,
                         local_species_population)
        self.algorithm = 'FBA'
        if time_step <= 0:
            raise MultialgorithmError("time_step must be positive, but is {}".format(time_step))
        self.time_step = time_step
        self.options = options

        # log initialization data
        self.log_with_time("init: id: {}".format(id))
        self.log_with_time("init: time_step: {}".format(str(time_step)))

        self.metabolismProductionReaction = None
        self.exchangedSpecies = None

        self.cobraModel = None
        self.thermodynamicBounds = None
        self.exchangeRateBounds = None

        self.defaultFbaBound = 1e15
        self.reactionFluxes = np.zeros(0)
Ejemplo n.º 21
0
    def get_concentrations(self, compartment_id=None):
        """ Get species concentrations at checkpoint times

        Args:
            compartment_id (:obj:`str`, optional): if provided, obtain concentrations for species in
                `compartment_id`; otherwise, return the concentrations of all species

        Returns:
            :obj:`pandas.DataFrame`: the concentrations of species at checkpoint times, filtered
                by `compartment_id` if it's provided

        Raises:
            :obj:`MultialgorithmError`: if no species are in the compartment
        """
        populations = self.get('populations')
        self._check_component('populations')
        if compartment_id is None:
            # iterate over species in populations, dividing by the right compartment
            # (as of 0.25.3 pandas doesn't support joins between two MultiIndexes)
            pop_div_vol = populations.copy()
            for species_id in populations.columns.values:
                _, compartment_id = ModelUtilities.parse_species_id(species_id)
                pop_div_vol.loc[:, species_id] = pop_div_vol.loc[:, species_id] / \
                    self.get_volumes(compartment_id=compartment_id)
            concentrations = pop_div_vol / Avogadro
            return (concentrations)

        else:
            compartment_vols = self.get_volumes(compartment_id=compartment_id)
            # filter to populations for species in compartment_id
            filter = f'\[{compartment_id}\]$'
            filtered_populations = populations.filter(regex=filter)
            if filtered_populations.empty:  # pragma: no cover
                raise MultialgorithmError(
                    f"No species found in compartment '{compartment_id}'")
            concentrations = filtered_populations.div(compartment_vols,
                                                      axis='index') / Avogadro
            return (concentrations)
Ejemplo n.º 22
0
    def create_ode_solver(self, **options):
        """ Create a `scikits.odes` ODE solver that uses CVODE

        Args:
            options (:obj:`dict`): options for the solver;
                see https://github.com/bmcage/odes/blob/master/scikits/odes/sundials/cvode.pyx

        Returns:
            :obj:`scikits.odes.ode`: an ODE solver instance

        Raises:
            :obj:`MultialgorithmError`: if the ODE solver cannot be created
        """
        # use CVODE from LLNL's SUNDIALS project (https://computing.llnl.gov/projects/sundials)
        CVODE_SOLVER = 'cvode'
        solver = ode(CVODE_SOLVER,
                     self.right_hand_side,
                     old_api=False,
                     **options)
        if not isinstance(solver, ode):  # pragma: no cover
            raise MultialgorithmError(
                f"OdeSubmodel {self.id}: scikits.odes.ode() failed")
        return solver
Ejemplo n.º 23
0
    def get(self, component):
        """ Provide the specified `component`

        Args:
            component (:obj:`str`): the name of the component to return

        Returns:
            :obj:`object`: an object containing a component of this `RunResults`, as specified by `component`;
                simulation time series data are :obj:`pandas.DataFrame` or `pandas.Series` instances;
                simulation metadata are :obj:`dict` instances.

        Raises:
            :obj:`MultialgorithmError`: if `component` is not an element of `RunResults.COMPONENTS`
                or `RunResults.COMPUTED_COMPONENTS`
        """
        all_components = RunResults.COMPONENTS.union(
            RunResults.COMPUTED_COMPONENTS)
        if component not in all_components:
            raise MultialgorithmError(
                f"component '{component}' is not an element of {all_components}"
            )
        if component in RunResults.COMPUTED_COMPONENTS:
            return RunResults.COMPUTED_COMPONENTS[component](self)
        return self.run_results[component]
Ejemplo n.º 24
0
    def create_dynamic_submodels(self):
        """ Create dynamic submodels that access shared species

        Returns:
            :obj:`list`: list of the simulation's `DynamicSubmodel`\ s

        Raises:
            :obj:`MultialgorithmError`: if a submodel cannot be created
        """
        def get_options(self, submodel_class_name):
            if self.options is not None and submodel_class_name in self.options:
                return self.options[submodel_class_name]
            else:
                return {}

        # make the simulation's submodels
        simulation_submodels = {}
        for lang_submodel in self.model.get_submodels():

            if lang_submodel.id in self.skipped_submodels():
                continue

            # don't create a submodel with no reactions
            if not lang_submodel.reactions:
                warnings.warn(
                    f"not creating submodel '{lang_submodel.id}': no reactions provided",
                    MultialgorithmWarning)
                continue

            if are_terms_equivalent(
                    lang_submodel.framework,
                    onto['WC:stochastic_simulation_algorithm']):
                simulation_submodel = SsaSubmodel(
                    lang_submodel.id, self.dynamic_model,
                    list(lang_submodel.reactions),
                    lang_submodel.get_children(kind='submodel',
                                               __type=Species),
                    self.get_dynamic_compartments(lang_submodel),
                    self.local_species_population,
                    **get_options(self, 'SsaSubmodel'))

            elif are_terms_equivalent(lang_submodel.framework,
                                      onto['WC:next_reaction_method']):
                simulation_submodel = NrmSubmodel(
                    lang_submodel.id, self.dynamic_model,
                    list(lang_submodel.reactions),
                    lang_submodel.get_children(kind='submodel',
                                               __type=Species),
                    self.get_dynamic_compartments(lang_submodel),
                    self.local_species_population,
                    **get_options(self, 'NrmSubmodel'))

            elif are_terms_equivalent(
                    lang_submodel.framework,
                    onto['WC:dynamic_flux_balance_analysis']):
                # TODO(Arthur): make DFBA submodels work
                simulation_submodel = DfbaSubmodel(
                    lang_submodel.id, self.dynamic_model,
                    list(lang_submodel.reactions),
                    lang_submodel.get_children(kind='submodel',
                                               __type=Species),
                    self.get_dynamic_compartments(lang_submodel),
                    self.local_species_population,
                    self.wc_sim_config.dfba_time_step,
                    **get_options(self, 'DfbaSubmodel'))

            elif are_terms_equivalent(
                    lang_submodel.framework,
                    onto['WC:ordinary_differential_equations']):
                simulation_submodel = OdeSubmodel(
                    lang_submodel.id, self.dynamic_model,
                    list(lang_submodel.reactions),
                    lang_submodel.get_children(kind='submodel',
                                               __type=Species),
                    self.get_dynamic_compartments(lang_submodel),
                    self.local_species_population,
                    self.wc_sim_config.ode_time_step,
                    **get_options(self, 'OdeSubmodel'))

            elif are_terms_equivalent(
                    lang_submodel.framework,
                    onto['WC:deterministic_simulation_algorithm']):
                # a deterministic simulation algorithm, used for testing
                simulation_submodel = DsaSubmodel(
                    lang_submodel.id, self.dynamic_model,
                    list(lang_submodel.reactions),
                    lang_submodel.get_children(kind='submodel',
                                               __type=Species),
                    self.get_dynamic_compartments(lang_submodel),
                    self.local_species_population,
                    **get_options(self, 'DsaSubmodel'))

            else:
                raise MultialgorithmError(
                    f"Unsupported lang_submodel framework '{lang_submodel.framework}'"
                )

            simulation_submodels[simulation_submodel.id] = simulation_submodel

            # add the submodel to the simulation
            self.simulation.add_object(simulation_submodel)

        return simulation_submodels
Ejemplo n.º 25
0
    def run(self,
            time_max,
            results_dir=None,
            progress_bar=True,
            checkpoint_period=None,
            seed=None,
            ode_time_step=None,
            dfba_time_step=None,
            profile=False,
            submodels_to_skip=None,
            verbose=True,
            options=None):
        """ Run one simulation

        Args:
            time_max (:obj:`float`): the maximum time of a simulation; a stop condition may end it
                earlier (sec)
            results_dir (:obj:`str`, optional): path to a directory in which results are stored
            progress_bar (:obj:`bool`, optional): whether to show the progress of a simulation in
                in a real-time bar on a terminal
            checkpoint_period (:obj:`float`, optional): the period between simulation state checkpoints (sec)
            ode_time_step (:obj:`float`, optional): time step length of ODE submodel (sec)
            dfba_time_step (:obj:`float`, optional): time step length of dFBA submodel (sec)
            profile (:obj:`bool`, optional): whether to output a profile of the simulation's performance
                created by a Python profiler
            seed (:obj:`object`, optional): a seed for the simulation's `numpy.random.RandomState`;
                if provided, `seed` will reseed the simulator's PRNG
            submodels_to_skip (:obj:`list` of :obj:`str`, optional): submodels that should not be run,
                identified by their ids
            verbose (:obj:`bool`, optional): whether to print success output
            options (:obj:`dict`, optional): options for submodels, passed to `MultialgorithmSimulation`

        Returns:
            :obj:`tuple` of (`int`, `str`): number of simulation events, pathname of directory
                containing the results, or :obj:`tuple` of (`int`, `None`): number of simulation events,
                `None` if `results_dir is None`, or :obj:`tuple` of (`pstats.Stats`, `None`): profile
                stats, `None` if `profile is True`

        Raises:
            :obj:`MultialgorithmError`: if the simulation raises an exception
        """
        self._prepare()

        # create simulation configurations
        # create and validate DE sim configuration
        self.de_sim_config = SimulationConfig(time_max,
                                              output_dir=results_dir,
                                              progress=progress_bar,
                                              profile=profile)
        self.de_sim_config.validate()

        # create and validate WC configuration
        self.wc_sim_config = WCSimulationConfig(
            de_simulation_config=self.de_sim_config,
            random_seed=seed,
            ode_time_step=ode_time_step,
            dfba_time_step=dfba_time_step,
            checkpoint_period=checkpoint_period,
            submodels_to_skip=submodels_to_skip,
            verbose=verbose)
        self.wc_sim_config.validate()

        # create author metadata for DE sim
        try:
            username = getpass.getuser()
        except KeyError:  # pragma: no cover
            username = '******'
        self.author_metadata = AuthorMetadata(
            name='Unknown name',
            email='Unknown email',
            username=username,
            organization='Unknown organization')

        # create WC sim metadata
        wc_simulation_metadata = WCSimulationMetadata(self.wc_sim_config)
        if self.model_path is not None:
            wc_simulation_metadata.set_wc_model_repo(self.model_path)

        if seed is not None:
            RandomStateManager.initialize(seed=seed)

        # create a multi-algorithmic simulator
        multialgorithm_simulation = MultialgorithmSimulation(
            self.model, self.wc_sim_config, options)
        self.simulation_engine, self.dynamic_model = multialgorithm_simulation.build_simulation(
        )
        self.simulation_engine.initialize()

        # set stop_condition after the dynamic model is created
        self.de_sim_config.stop_condition = self.dynamic_model.get_stop_condition(
        )

        # run simulation
        try:
            # provide DE config and author metadata to DE sim
            simulate_rv = self.simulation_engine.simulate(
                sim_config=self.de_sim_config,
                author_metadata=self.author_metadata)

            # add WC sim metadata to the output after the simulation, which requires an empty output dir
            # TODO: have simulation_engine.simulate() allow certain files in self.de_sim_config.output_dir, and move
            # this code above
            if self.de_sim_config.output_dir is not None:
                WCSimulationMetadata.write_dataclass(
                    wc_simulation_metadata, self.de_sim_config.output_dir)

            if profile:
                stats = simulate_rv
                return stats, None
            else:
                num_events = simulate_rv

        except SimulatorError as e:  # pragma: no cover
            raise MultialgorithmError(
                f'Simulation terminated with simulator error:\n{e}')
        except BaseException as e:  # pragma: no cover
            raise MultialgorithmError(
                f'Simulation terminated with error:\n{e}')

        if verbose:
            print(f'Simulated {num_events} events')
        if results_dir:
            # summarize results in an HDF5 file in results_dir
            RunResults(results_dir)
            if verbose:
                print(f"Saved checkpoints and run results in '{results_dir}'")
            return (num_events, results_dir)
        else:
            return (num_events, None)
Ejemplo n.º 26
0
    def prepare(self):
        """ Prepare this dynamic expression for simulation

        Because they refer to each other, all :obj:`DynamicExpression`\ s must be created before any
        of them are prepared.

        Raises:
            :obj:`MultialgorithmError`: if a Python function used in `wc_lang_expression` does not exist
        """

        # create self.wc_sim_tokens, which contains WcSimTokens that refer to other DynamicExpressions
        self.wc_sim_tokens = []
        # optimization: combine together adjacent obj_tables_token.tok_codes other than obj_id
        next_static_tokens = ''
        function_names = set()

        i = 0
        while i < len(self.wc_lang_expression._obj_tables_tokens):
            obj_tables_token = self.wc_lang_expression._obj_tables_tokens[i]
            if obj_tables_token.code == ObjTablesTokenCodes.math_func_id:
                function_names.add(obj_tables_token.token_string)
            if obj_tables_token.code in self.NON_LANG_OBJ_ID_TOKENS:
                next_static_tokens = next_static_tokens + obj_tables_token.token_string
            elif obj_tables_token.code == ObjTablesTokenCodes.obj_id:
                if next_static_tokens != '':
                    self.wc_sim_tokens.append(
                        WcSimToken(SimTokCodes.other, next_static_tokens))
                    next_static_tokens = ''
                try:
                    dynamic_expression = DynamicComponent.get_dynamic_component(
                        obj_tables_token.model, obj_tables_token.model_id)
                except:
                    raise MultialgorithmError(
                        "'{}.{} must be prepared to create '{}''".format(
                            obj_tables_token.model.__class__.__name__,
                            obj_tables_token.model_id, self.id))
                self.wc_sim_tokens.append(
                    WcSimToken(SimTokCodes.dynamic_expression,
                               obj_tables_token.token_string,
                               dynamic_expression))
            else:  # pragma: no cover
                assert False, "unknown code {} in {}".format(
                    obj_tables_token.code, obj_tables_token)
            # advance to the next token
            i += 1
        if next_static_tokens != '':
            self.wc_sim_tokens.append(
                WcSimToken(SimTokCodes.other, next_static_tokens))
        # optimization: to conserve memory, delete self.wc_lang_expression
        del self.wc_lang_expression

        # optimization: pre-allocate and pre-populate substrings for the expression to eval
        self.expr_substrings = []
        for sim_token in self.wc_sim_tokens:
            if sim_token.code == SimTokCodes.other:
                self.expr_substrings.append(sim_token.token_string)
            else:
                self.expr_substrings.append('')

        # optimization: pre-allocate Python functions in namespace
        self.local_ns = {}
        for func_name in function_names:
            if func_name in globals()['__builtins__']:
                self.local_ns[func_name] = globals()['__builtins__'][func_name]
            elif hasattr(globals()['math'], func_name):
                self.local_ns[func_name] = getattr(globals()['math'],
                                                   func_name)
            else:  # pragma no cover, because only known functions are allowed in model expressions
                raise MultialgorithmError(
                    "loading expression '{}' cannot find function '{}'".format(
                        self.expression, func_name))
Ejemplo n.º 27
0
 def __setattr__(self, name, value):
     """ Validate an attribute when it is changed """
     try:
         super().__setattr__(name, value)
     except TypeError as e:
         raise MultialgorithmError(e)
Ejemplo n.º 28
0
    def __init__(self, model, species_population, dynamic_compartments):
        """ Prepare a `DynamicModel` for a discrete-event simulation

        Args:
            model (:obj:`Model`): the description of the whole-cell model in `wc_lang`
            species_population (:obj:`LocalSpeciesPopulation`): the simulation's species population store
            dynamic_compartments (:obj:`dict`): the simulation's :obj:`DynamicCompartment`\ s, one
                for each compartment in `model`

        Raises:
            :obj:`MultialgorithmError`: if the model has no cellular compartments
        """
        self.id = model.id
        self.dynamic_compartments = dynamic_compartments
        self.species_population = species_population
        self.num_submodels = len(model.get_submodels())

        # determine cellular compartments
        self.cellular_dyn_compartments = []
        for dynamic_compartment in dynamic_compartments.values():
            if dynamic_compartment.biological_type == onto[
                    'WC:cellular_compartment']:
                self.cellular_dyn_compartments.append(dynamic_compartment)
        if dynamic_compartments and not self.cellular_dyn_compartments:
            raise MultialgorithmError(
                f"model '{model.id}' must have at least 1 cellular compartment"
            )

        # === create dynamic objects that are not expressions ===
        # create dynamic parameters
        self.dynamic_parameters = {}
        for parameter in model.parameters:
            self.dynamic_parameters[parameter.id] = \
                DynamicParameter(self, self.species_population, parameter, parameter.value)

        # create dynamic species
        self.dynamic_species = {}
        for species in model.get_species():
            self.dynamic_species[species.id] = \
                DynamicSpecies(self, self.species_population, species)

        # === create dynamic expressions ===
        # create dynamic observables
        self.dynamic_observables = {}
        for observable in model.observables:
            self.dynamic_observables[observable.id] = \
                DynamicObservable(self, self.species_population, observable,
                                  observable.expression._parsed_expression)

        # create dynamic functions
        self.dynamic_functions = {}
        for function in model.functions:
            self.dynamic_functions[function.id] = \
                DynamicFunction(self, self.species_population, function,
                                function.expression._parsed_expression)

        # create dynamic stop conditions
        self.dynamic_stop_conditions = {}
        for stop_condition in model.stop_conditions:
            self.dynamic_stop_conditions[stop_condition.id] = \
                DynamicStopCondition(self, self.species_population, stop_condition,
                                     stop_condition.expression._parsed_expression)

        # create dynamic rate laws
        self.dynamic_rate_laws = {}
        for rate_law in model.rate_laws:
            self.dynamic_rate_laws[rate_law.id] = \
                DynamicRateLaw(self, self.species_population, rate_law,
                                     rate_law.expression._parsed_expression)

        # create dynamic dFBA Objectives
        self.dynamic_dfba_objectives = {}
        '''
        # todo: fix: 'DfbaObjReaction.Metabolism_biomass must be prepared to create 'dfba-obj-test_submodel''
        for dfba_objective in model.dfba_objs:
            self.dynamic_dfba_objectives[dfba_objective.id] = \
                DynamicDfbaObjective(self, self.species_population, dfba_objective,
                                     dfba_objective.expression._parsed_expression)
        '''

        # prepare dynamic expressions
        for dynamic_expression_group in [
                self.dynamic_observables, self.dynamic_functions,
                self.dynamic_stop_conditions, self.dynamic_rate_laws,
                self.dynamic_dfba_objectives
        ]:
            for dynamic_expression in dynamic_expression_group.values():
                dynamic_expression.prepare()