Source code for cayenne.model_io

"""
    The class that handles model IO
"""

import numpy as np

import antimony as sb


[docs]class ModelError(Exception): pass
[docs]class InitialStateError(ModelError): pass
[docs]class RateConstantError(ModelError): pass
[docs]class ChemFlagError(ModelError): pass
[docs]class VolumeError(ModelError): pass
[docs]class ModelIO: """ Class for loading and parsing models Parameters --------- model_contents : str Either the model string or the file path content_type : str, {"ModelString", "ModelFile"} The type of the model Attributes ---------- react_stoic: (ns, nr) ndarray A 2D array of the stoichiometric coefficients of the reactants. Reactions are columns and species are rows. prod_stoic: (ns, nr) ndarray A 2D array of the stoichiometric coefficients of the products. Reactions are columns and species are rows. init_state: (ns,) ndarray A 1D array representing the initial state of the system. k_det: (nr,) ndarray A 1D array representing the deterministic rate constants of the system. volume: float, optional The volume of the reactor vessel which is important for second and higher order reactions. Defaults to 1 arbitrary units. chem_flag: bool, optional If True, divide by Na (Avogadro's constant) while calculating stochastic rate constants. Defaults to ``False``. """ def __init__(self, model_contents: str, content_type: str) -> None: if content_type == "ModelString": er_code = sb.loadAntimonyString(model_contents) elif content_type == "ModelFile": er_code = sb.loadAntimonyFile(model_contents) else: raise KeyError(f"Unsupported content_type: {content_type}") self.er_code = er_code if self.er_code == -1: error_msg = "Error while parsing model. Model variable names " error_msg += "might be antimony keywords (see docs at https://" error_msg += "cayenne.readthedocs.io/en/latest/tutorial.html)." raise ModelError(error_msg) self.sb_module = sb.getMainModuleName() self._parse_model() @staticmethod def _create_stoic_mat(ns, nr, name_list, stoic_tuple, species_names): """ Function to create the stoichiometric matrix """ stoic_mat = np.zeros([ns, nr], dtype=np.int) for index, (names, stoics) in enumerate(zip(name_list, stoic_tuple)): for a_name, a_stoic in zip(names, stoics): species_index = species_names.index(a_name) stoic_mat[species_index, index] += int(a_stoic) return stoic_mat def _parse_model(self): """ Parse model contents """ react_stoic_tuple = sb.getReactantStoichiometries(self.sb_module) react_names = sb.getReactantNames(self.sb_module) prod_names = sb.getProductNames(self.sb_module) prod_stoic_tuple = sb.getProductStoichiometries(self.sb_module) # 0:all, 1:speciescounts, 2:rateconstants, 6:rxnrateequations, 9:compartmentvols species_names = sb.getSymbolNamesOfType(self.sb_module, 1) self.species_names = species_names rxn_names = sb.getSymbolNamesOfType(self.sb_module, 6) self.rxn_names = rxn_names ns = len(species_names) nr = sb.getNumReactions(self.sb_module) # Stochastic matrices self.react_stoic = self._create_stoic_mat( ns, nr, react_names, react_stoic_tuple, species_names ) self.prod_stoic = self._create_stoic_mat( ns, nr, prod_names, prod_stoic_tuple, species_names ) # Initial states init_state_values = sb.getSymbolInitialAssignmentsOfType(self.sb_module, 1) if "" in init_state_values: raise InitialStateError("Missing initial value for one of the species.") self.init_state = np.array(init_state_values, dtype=np.int64) # Rate constants rxn_rateeqns = sb.getSymbolEquationsOfType(self.sb_module, 6) rxn_rate_names = list(sb.getSymbolNamesOfType(self.sb_module, 2)) rxn_rate_values = sb.getSymbolInitialAssignmentsOfType(self.sb_module, 2) rxn_rate_dict = dict(zip(rxn_rate_names, rxn_rate_values)) # Chem flag try: self.chem_flag = True if rxn_rate_dict["chem_flag"] == "true" else False except KeyError: raise ChemFlagError("The chem flag was not specified in the model.") # Check rate constant specifications del rxn_rate_dict["chem_flag"] rxn_rate_names.remove("chem_flag") if "" in rxn_rateeqns: raise RateConstantError("Missing rate constant for one of the reactions.") for rxn_rateeqn in rxn_rateeqns: if rxn_rateeqn not in rxn_rate_names: raise RateConstantError( f"{rxn_rateeqn} doesn't match any rate constant." ) # kdet try: self.k_det = np.array( [rxn_rate_dict[rre] for rre in rxn_rateeqns], dtype=float ) except KeyError: raise RateConstantError( "You are missing a numerical value for one of the rate constants." ) # Volume try: self.volume = int( sb.getSymbolInitialAssignmentsOfType(self.sb_module, 9)[0] ) except IndexError: raise VolumeError("Missing compartment information") @property def args(self): """ Returns the attributes of the ModelIO class """ return ( self.species_names, self.rxn_names, self.react_stoic, self.prod_stoic, self.init_state, self.k_det, self.chem_flag, self.volume, )
[docs] @classmethod def translate_sbml(cls, sbml_file: str): """ Translate SBML file to Antimony model specification. cayenne's model specification is loosely based on Antimony's model specification. """ er_code = sb.loadSBMLFile(sbml_file) if er_code == -1: raise ModelError("Error while parsing model") sb_module = sb.getMainModuleName() sb_string = sb.getAntimonyString(sb_module) return sb_string