Exemplo n.º 1
0
    def _release(self, end: Finished, fullrelease: bool = True) -> str:
        """
        Clean data before run ending.

        A double release will raise an InternalError.
        Intended to be launched from self.run, not manually

        @param end: ending message
        @type end: Finished
        @param fullrelease: if true, also clean memory (Default value = True)
        @type fullrelease: bool
        @return: end message
        @rtype: str
        """
        if self.initialized:
            self.statistic.end(end)
            self.statistic.calcsnapshot(final=True)
            if fullrelease:
                self.crn.close()
                self.param.unlock()
                self.initialized = False
            self.status.gcclean()
            self.signcatch.reset()
            LOGGER.info(f"System {'fully ' if fullrelease else ''}cleaned")
            return f"{end} ({LOGGER.runtime} s)"
        raise InternalError("Attempt to release non-initialized system!")
Exemplo n.º 2
0
    def __init__(self,
                 model: Model,
                 categorize: bool = True,
                 dropmode: str = "drop"):
        """Create a collection from a model (for categorizing and building objects).

        @param model: Model containing all rules
        @type model: Model
        @param categorize: shall the objects be automatically categorized (Default value = True)
        @type categorize: bool
        @param dropmode: how the unactivated object are dealt
            ("drop"=> removed, "keep"=> kept in pool)
            (Default value = "drop")
        @type dropmode: str

        """
        self.model: Model = model
        """rule model"""
        self.dropmode: str = dropmode
        """drop mode 'keep' or 'drop'"""
        self.pool: Dict[K, T] = {}
        """pool of all objects"""
        self.categories: Dict[str, Set[K]] = defaultdict(set)
        """categories of objects"""
        self.active: Dict[K, T] = self.pool if self.dropmode == "drop" else {}
        """pool of active objects"""
        self.categorize: bool = categorize
        """shall the objects be categorized"""
        LOGGER.debug(
            f"Created {self} as drop={self.dropmode}, with pool of type {type(self.pool)}"
        )
Exemplo n.º 3
0
    def choose(self) -> Tuple[Any, float]:
        """Choose a probabilistic event according to Gillespie's algorithm.

        @return: the chosen object, and the delta_t after which the event occured
        @rtype: Tuple[Any, float]

        @raise RoundError: if something bad happened (most likely being a rounding error somewhere)

        """
        # First choose a random line in the probability map
        try:
            # chosen = random.choice(self._mapobj, p=self._problist / self.probtot)
            chosen_num = choice(self._problist,
                                self.probtot * self.sysrand.random())
            chosen = self._mapobj[chosen_num]
            if chosen_num == -1:
                if self.probtot == 0.0:
                    raise NoMore("choice() had nothing to choose!")
                raise RoundError(
                    "choice() couldn't find a suitable object."
                    f"probtot={self.probtot}=?={self._problist.sum()}; "
                    f"problist={self._problist})")
            delta_t = np.log(1 / self.sysrand.random()) / self.probtot
            if chosen is None:
                LOGGER.error(
                    f"Badly destroyed reaction from {self._mapobj} with proba {self._problist}"
                )
            return (chosen, delta_t)
        except ValueError as err:
            raise RoundError(
                f"(reason: {err}; "
                f"probtot={self.probtot}=?={self._problist.sum()}; "
                f"problist={self._problist})")
Exemplo n.º 4
0
 def set_param(self, **kwd: Any) -> None:
     """Set run parameters, overriding the previous ones."""
     try:
         self.param.set_param(**kwd)
     except LockedError:
         raise InternalError("Parameter set after initialization")
     LOGGER.info(f"Parameters changed: {self.param}")
Exemplo n.º 5
0
 def _create_rules(self) -> None:
     """Read and create rules."""
     for rulename in self.reactions:
         # Get rule from parameter file
         if rulename in self.param.rules:
             ruleparam = self.param.rules[rulename]
             try:
                 # then create it from rule module
                 rule = Rule(
                     name=rulename,
                     reactants=tuple(ruleparam.reactants),
                     builder=(
                         self.rulepath[ruleparam.builder_func],
                         self.rulepath[ruleparam.builder_const],
                         self.rulepath[ruleparam.builder_variant],
                     ),
                     descr=ruleparam.descr,
                     parameters=self.parameters,
                     robust=ruleparam.robust,
                 )
             except KeyError:
                 # raise an error if the rule from file is not in the module
                 raise InitError(
                     f"The rule '{rulename}' from '{self.modelparam}'"
                     f"is not defined in '{self.rulepath}'"
                 )
             # Register the created rule
             self._add_rule(rulename, rule)
         else:
             LOGGER.warning(
                 f"Reaction '{rulename}' not found in {self.rulepath}, ignored."
             )
Exemplo n.º 6
0
    def set_param(self, key: str, val: float, terminate: bool = True) -> None:
        """Set a parameter value.

        This will automatically update parameters defined by a relation

        If an attempt is done to fix the value of a parameter defined by a
        relation, the request will be ignored *except* if the 'terminate'
        flag is set to False (do not use this flag manually, this is intended to be used
        by _param_init; direct use may result in an inconsistent set of parameters)

        @param key: parameter name
        @type key: str
        @param val: parameter value
        @type val: float
        @param terminate: do not use manually (see above)
        @type terminate: bool

        @raise KeyError: if the parameter does not exists

        """
        if terminate and key in self._relation:
            LOGGER.warning(
                f"'{key}' was set by a relation, direct setting request to '{val}' ignored"
            )
        elif key in self._paramdict:
            if key not in self._updating:
                self._paramdict[key] = val
                self._updating.append(key)
                self._param_init()
        else:
            raise KeyError(f"Key {key} not registered")
        if terminate:
            self._updating.clear()
Exemplo n.º 7
0
    def __init__(self, param: Param):
        """Create a Crn object from a L{Param} parameter object.

        @param param: parameter object
        @type param: Param

        """
        # declarations
        self.probalist: Probalist
        """list of probalities"""
        self.comp_collect: CollectofCompound
        """compounds collection"""
        self.reac_collect: CollectofReaction
        """reactions collection"""
        # update trackers
        self._reac_update: Set[Reaction] = set()
        """Track the reactions to be updated in batch"""
        self._comp_update: Dict[Compound, int] = {}
        """Track the compounds to be updated in batch"""
        # Create Crn objects
        self.model: Model = Model(param.rulemodel, param.reactions, param.parameters)
        """Model describing the set of rules"""
        self.vol: float = param.vol
        """System volume"""
        self.dropmode: str = param.dropmode
        """dropmode flag (do we 'drop' or 'keep' inactive reactions?)"""
        self.init_collect()
        for compound, pop in param.init.items():
            self.comp_collect[compound].change_pop(pop)
        self.update()
        LOGGER.debug(f"Initialized with {param}")
Exemplo n.º 8
0
 def close(self) -> None:
     """Write all remaining data  in hdf5 file, clean it, then close it."""
     LOGGER.info(f"File {self.writer.filename} to be written and closed...")
     self.writesnap()
     self.writemap()
     self.writer.close()
     LOGGER.info(f"...written and closed, done.")
Exemplo n.º 9
0
 def writesnap(self) -> None:
     """Write all previous snapshots to hdf5."""
     # Correct snapshot sizes
     nbsnap = MPI_STATUS.max(self._nbsnap)
     nbcomp = MPI_STATUS.max(self._nbcomp)
     nbreac = MPI_STATUS.max(self._nbreac)
     LOGGER.debug(f"resize snapshot with {nbsnap}-{nbcomp}-{nbreac}")
     self.writer.snapsize(nbcomp, nbreac, nbsnap)
     # Write snapshots
     for col, (time, comp, reac) in enumerate(
             zip(self._snaptimes, self._snapcomp, self._snapreac)):
         self.writer.add_snapshot(comp, reac, col, time)
Exemplo n.º 10
0
 def writestat(self) -> None:
     """Write general statistics at present time to hdf5."""
     res = (self.status.info + self.concentrations + [
         self.crn.collstat(
             collection=stats.collection,
             prop=stats.prop,
             weight=stats.weight,
             method=stats.method,
             full=stats.full,
         ) for stats in self.param.statparam.values()
     ])
     self.writer.add_data(res)
     LOGGER.debug(str(res))
Exemplo n.º 11
0
    def checkmem(self) -> None:
        """
        Check if the process reached its memory limit.

        If reached, close the MPI gate for 'oom' reason
        for selectively stopping some threads at the next
        gathering at the MPI gate.
        """
        if self.gcperio:
            gc.collect()
        if self.memuse > self.maxmem / MPI_GATE.mem_divide:
            LOGGER.warning(
                f"{self.memuse}>{self.maxmem/MPI_GATE.mem_divide}, call oom")
            MPI_GATE.close("oom")
Exemplo n.º 12
0
    def run(self, release: bool = True) -> str:
        """
        Launch a run.

        The final state can be kept by setting 'release' to false (useful for run directly launched
        from the command line for further direct data manipulation). It is by default released in
        order to release memory as soon as a run is finished during batch runs.

        @param release: Shall the final state be released at run end
        @type release: bool
        @return: end message
        @rtype: str
        """
        LOGGER.reset_timer()
        if MPI_STATUS.ismpi:
            LOGGER.info(f"Launching MPI run from thread #{MPI_STATUS.rank}")
        else:
            LOGGER.info("Launching single run.")
        # Setup working environment
        self._initialize()
        # Ready, let's run
        LOGGER.info(f"Run #{MPI_STATUS.rank}={getpid()} launched")
        with MPI_GATE.launch():
            # Do not stop except if asked to exit the gate
            while MPI_GATE.cont:
                try:
                    # Log stat info
                    self.status.logstat()
                    # perform a step
                    self._process()
                    # Write data statistics
                    self.statistic.calc()
                    # Check status before next step
                    self.status.checkstep()
                    # Pass gate checkpoint
                    MPI_GATE.checkpoint()
                # exit the gate because the work is finished.
                except Finished as the_end:
                    end = self._release(the_end, release)
                    break
            # exit the gate because asked by collective decision.
            else:
                end = self._release(
                    OOMError("Stopped by kind request of OOM killer"), release)
        # Write and log final data, then exit.
        LOGGER.info(f"Run #{MPI_STATUS.rank}={getpid()} finished")
        self.statistic.close()
        return end
Exemplo n.º 13
0
    def unregister_reaction(self, reaction: Reaction) -> None:
        """Unregister a reaction from the compound list of the reactions it is involved in as a reactant.

        (unregistered from self.reactions)

        Called at reaction deactivation.

        @param reaction: reaction object to be unregistered
        @type reaction: Reaction

        """
        try:
            self.reactions.remove(reaction)
        except KeyError:
            LOGGER.debug(
                f"Tried to unregister twice {reaction} from {self} (p={self.pop})"
            )
Exemplo n.º 14
0
    def __init__(self, description: str, crn: "Crn"):
        """Create the reaction from its 'description', to be linked to the parent 'crn'.

        The description is simply the compound name

        @param description: object description
        @type description: str
        @param crn: parent chemical reaction network
        @type crn: Crn

        """
        super().__init__(description, crn)
        if self.description == "":
            LOGGER.error("Created empty compound!!!")
        self.reactions: Set[Reaction] = set()
        """Set of reactions in which the compound is a reactant"""
        self.pop: int = 0
        """compound population"""
Exemplo n.º 15
0
    def add_relation(self, key: str, relation: Paramrel) -> None:
        """Add a new parameter and set it by a relation.

        If the key already exists, it will be overriden.

        @param key: parameter name
        @type key: str
        @param relation: relation function
        @type relation: Paramrel

        """
        try:
            self.add_param(key)
        except KeyError:
            LOGGER.warning(
                f"Setting '{key}' relation will override already set value ({self[key]})"
            )
        self._relation[key] = relation
        self._param_init()
Exemplo n.º 16
0
    def __init__(self, param: Param):
        """
        Generate files structures from parameters.

        @param param: parameters
        @type param: Param
        """
        self.name: str = param.name
        """run name"""
        # self.timeformat = param.timeformat
        self.savedir: str = param.savedir if param.savedir else path.curdir
        """save folder"""
        if not path.isdir(self.savedir):
            raise NotAFolder("Bad folder name {self.savedir}")
        self.logdir: str = param.logdir
        """log folder"""
        if not path.isdir(self.logdir):
            LOGGER.debug(
                "Logs will be sent to standard output because {self.logdir} is not a folder."
            )
Exemplo n.º 17
0
 def _process(self) -> None:
     """Perform a run step."""
     # Check if a cleanup should be done
     if self.param.autoclean:
         self.crn.clean()
     # Process self.maxsteps times
     for _ in repeat(None, self.param.maxsteps):
         # ... but stop if step ends
         if self.status.finished:
             break
         # ... or if process is stopped by a signal
         if not self.signcatch.alive:
             raise Interrupted(
                 f" by {self.signcatch.signal} at t={self.status.time}")
         # perform a random event
         self.status.inc(self.crn.stepping())
     else:
         #  had to performed loop until maxsteps
         LOGGER.warning(
             f"maxsteps per process (={self.param.maxsteps}) too low")
Exemplo n.º 18
0
    def _initialize(self) -> None:
        """Initialize the system before starting the run.

        Intended to be launched from self.run, not manually

        @raise InternalError: in case of double initialization.

        """
        if not self.initialized:
            self.signcatch.listen()
            self.status.initialize(self.param)
            self.crn = Crn(self.param)
            self.param.lock()
            self.statistic = Statistic(self.crn, self.writer, self.param,
                                       self.status, self.comment)
            self.statistic.startwriter()
            self.initialized = True
            LOGGER.info("System fully initialized")
        else:
            raise InternalError(
                "Attempt to initialize already initialized system!")
Exemplo n.º 19
0
    def __init__(
        self,
        param: Param,
    ):
        """Create a System from parameters 'param'.

        @param param: parameters
        @type param: Param
        """
        LOGGER.debug("Creating the system.")
        np.seterr(divide="ignore", invalid="ignore")
        self.initialized = False
        """Initialized flag"""
        self.param: Param = param
        """run parameters"""
        MPI_STATUS.init(self.param.timeformat)
        self.output: Output = Output(self.param)
        """Access to output files and folders"""
        self.writer: ResultWriter = ResultWriter(self.output.h5file,
                                                 self.param.maxstrlen,
                                                 self.param.lengrow)
        """Writer to HDF5 file."""
        self.writer.init_log(self.param.maxlog)
        LOGGER.setlevel(self.param.loglevel)
        LOGGER.connect(self.output.logfile)
        LOGGER.setsaver(self.writer)
        LOGGER.timeformat = self.param.timeformat
        self.signcatch: SignalCatcher = SignalCatcher()
        """Signal catcher for dealing with system interruptions"""
        self.status: RunStatus = RunStatus()
        """Interface to run status"""
        self.comment = self.param.comment
        """run comment"""
        MPI_GATE.init(taginit=100, sleeptime=param.sleeptime)
        LOGGER.info("System created")
        self.crn: Crn
        """Chemical Reaction Network"""
        self.statistic: Statistic
        """interface to run statistics"""
Exemplo n.º 20
0
    def end(self, the_end: Finished) -> None:
        """Write ending message to hdf5.

        @param the_end: ending message
        @type the_end: Finished
        """
        self.writer.add_end(the_end, LOGGER.runtime)
        if isinstance(the_end, HappyEnding):
            LOGGER.info(str(the_end))
        elif isinstance(the_end, BadEnding):
            LOGGER.error(str(the_end))
        else:
            LOGGER.warning(str(the_end))
Exemplo n.º 21
0
 def _create_descriptors(self) -> None:
     """Create the descriptors."""
     for rel in self.param.relations:
         try:
             self.parameters.add_relation(rel, self.rulepath[rel])
         except KeyError:
             LOGGER.error(f"relation '{rel}' not found in {self.modelparam}")
     for catname in self.param.categories:
         try:
             self.descriptor.add_cat(catname, self.rulepath[catname])
         except KeyError:
             LOGGER.error(f"category '{catname}' not found in {self.modelparam}")
     for propname in self.param.properties:
         try:
             self.descriptor.add_prop(propname, self.rulepath[propname])
         except KeyError:
             LOGGER.error(f"'[property '{propname}' not found in {self.modelparam}")
Exemplo n.º 22
0
 def logstat(self) -> None:
     """Log (at INFO level) run information."""
     LOGGER.info(
         f"#{self.step}'{self.dstep}'': {self.time} -> {self.tnext}  <{int(self.memuse)}Mb>"
     )