import copy
from bionetgen.main import BioNetGen
from bionetgen.core.exc import BNGModelError
from tempfile import TemporaryFile
from .bngparser import BNGParser
from .blocks import (
ActionBlock,
CompartmentBlock,
FunctionBlock,
MoleculeTypeBlock,
ObservableBlock,
ParameterBlock,
RuleBlock,
SpeciesBlock,
EnergyPatternBlock,
PopulationMapBlock,
)
# This allows access to the CLIs config setup
app = BioNetGen()
app.setup()
conf = app.config["bionetgen"]
def_bng_path = conf["bngpath"]
###### CORE OBJECT AND PARSING FRONT-END ######
[docs]class bngmodel:
"""
Main model object and entry point for model API. The goal of this
object is to generate and read the BNGXML of a given BNGL model
and give the user a pythonic interface to the resulting model object.
Usage: bngmodel(bng_model)
bngmodel(bng_model, BNGPATH)
Attributes
----------
active_blocks : list[str]
a list of the blocks that have been parsed in the model
bngparser : BNGParser
BNGParser object that's responsible for .bngl file reading and model setup
model_name : str
name of the model, generally set from the given BNGL file
recompile : bool
a tag to keep track if any changes have been made to the model
via the XML API by the user
changes : dict
a list of changes the user have made to the model
Methods
-------
reset_compilation_tags()
resets compilation tags of each block to keep track of any changes the user
makes to the model via the API
add_action(action_type, action_args)
adds the action of action_type with arguments given by the optional keyword
argument action_args, which is a dictionary where each element
is of the form "ArgumentName":ArgumentValue
write_model(model_name)
write the model in BNGL format to the path given
setup_simulator(sim_type)
sets up a simulator in bngmodel.simulator where the only current supported
type of simulator is libRR for libRoadRunner simulator.
"""
def __init__(self, bngl_model, BNGPATH=def_bng_path, generate_network=False):
self.active_blocks = []
# We want blocks to be printed in the same order every time
self.block_order = [
"parameters",
"compartments",
"molecule_types",
"species",
"observables",
"functions",
"energy_patterns",
"population_maps",
"rules",
"actions",
]
self.model_name = ""
self.model_path = bngl_model
self.bngparser = BNGParser(bngl_model, generate_network=generate_network)
self.bngparser.parse_model(self)
for block in self.block_order:
if block not in self.active_blocks:
self.add_empty_block(block)
# Check to see if there are no active blocks
# If not, model is most likely not in BNGL format
if not self.active_blocks:
print(
"WARNING: No active blocks. Please ensure model is in proper BNGL or BNG-XML format"
)
@property
def recompile(self):
recompile = False
for block in self.active_blocks:
recompile = recompile or getattr(self, block)._recompile
return recompile
@property
def changes(self):
changes = {}
for block in self.active_blocks:
changes[block] = getattr(self, block)._changes
return changes
def __str__(self):
"""
write the model to str
"""
model_str = ""
# gotta check for "before model" type actions
if hasattr(self, "actions"):
ablock = getattr(self, "actions")
if len(ablock.before_model) > 0:
for baction in ablock.before_model:
model_str += str(baction) + "\n"
model_str += "begin model\n"
for block in self.block_order:
# ensure we didn't get new items into a
# previously inactive block, if we did
# add them to the active blocks
if hasattr(self, block):
if len(getattr(self, block)) > 0:
if getattr(self, block).name not in self.active_blocks:
self.active_blocks.append(block)
# if we removed items from a block and
# it's now empty, we want to remove it
# from the active blocks
elif len(getattr(self, block)) == 0 and block in self.active_blocks:
self.active_blocks.remove(block)
# print only the active blocks
if block in self.active_blocks:
if block != "actions" and len(getattr(self, block)) > 0:
model_str += str(getattr(self, block))
model_str += "\nend model\n\n"
if "actions" in self.active_blocks:
model_str += str(self.actions)
return model_str
def __repr__(self):
return self.model_name
def __iter__(self):
active_ordered_blocks = [
getattr(self, i) for i in self.block_order if i in self.active_blocks
]
return active_ordered_blocks.__iter__()
[docs] def add_block(self, block):
bname = block.name.replace(" ", "_")
# TODO: fix this exception
if bname == "reaction_rules":
bname = "rules"
block_adder = getattr(self, "add_{}_block".format(bname))
block_adder(block)
[docs] def add_empty_block(self, block_name):
bname = block_name.replace(" ", "_")
# TODO: fix this exception
if bname == "reaction_rules":
bname = "rules"
block_adder = getattr(self, "add_{}_block".format(bname))
block_adder()
[docs] def add_parameters_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, ParameterBlock)
self.parameters = block
if "parameters" not in self.active_blocks:
self.active_blocks.append("parameters")
else:
self.parameters = ParameterBlock()
[docs] def add_compartments_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, CompartmentBlock)
self.compartments = block
if "compartments" not in self.active_blocks:
self.active_blocks.append("compartments")
else:
self.compartments = CompartmentBlock()
[docs] def add_molecule_types_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, MoleculeTypeBlock)
self.molecule_types = block
if "molecule_types" not in self.active_blocks:
self.active_blocks.append("molecule_types")
else:
self.molecule_types = MoleculeTypeBlock()
[docs] def add_species_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, SpeciesBlock)
self.species = block
if "species" not in self.active_blocks:
self.active_blocks.append("species")
else:
self.species = SpeciesBlock()
[docs] def add_observables_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, ObservableBlock)
self.observables = block
if "observables" not in self.active_blocks:
self.active_blocks.append("observables")
else:
self.observables = ObservableBlock()
[docs] def add_functions_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, FunctionBlock)
self.functions = block
if "functions" not in self.active_blocks:
self.active_blocks.append("functions")
else:
self.functions = FunctionBlock()
[docs] def add_rules_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, RuleBlock)
self.rules = block
if "rules" not in self.active_blocks:
self.active_blocks.append("rules")
else:
self.rules = RuleBlock()
[docs] def add_energy_patterns_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, EnergyPatternBlock)
self.energy_patterns = block
if "energy_patterns" not in self.active_blocks:
self.active_blocks.append("energy_patterns")
else:
self.energy_patterns = EnergyPatternBlock()
[docs] def add_population_maps_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, PopulationMapBlock)
self.population_maps = block
if "population_maps" not in self.active_blocks:
self.active_blocks.append("population_maps")
else:
self.population_maps = PopulationMapBlock()
[docs] def add_actions_block(self, block=None):
if block is not None:
# TODO: Transition to BNGErrors and logging
assert isinstance(block, ActionBlock)
self.actions = block
if "actions" not in self.active_blocks:
self.active_blocks.append("actions")
else:
self.actions = ActionBlock()
[docs] def add_action(self, action_type, action_args=[]):
# add actions block and to active list
if not hasattr(self, "actions"):
self.actions = ActionBlock()
if "actions" not in self.active_blocks:
self.active_blocks.append("actions")
self.actions.add_action(action_type, action_args)
[docs] def write_model(self, file_name):
"""
write the model to file
"""
with open(file_name, "w") as f:
f.write(str(self))
[docs] def setup_simulator(self, sim_type="libRR"):
"""
Sets up a simulator attribute that is a generic front-end
to all other simulators. At the moment only libroadrunner
is supported
"""
if sim_type == "libRR":
# we need to add writeSBML action for now
curr_actions = copy.deepcopy(self.actions)
self.actions.clear_actions()
self.add_action("generate_network", {"overwrite": 1})
self.add_action("writeSBML", {})
# temporary file
with TemporaryFile(mode="w+") as tpath:
# write the sbml
if not (
self.bngparser.bngfile.write_xml(
tpath, xml_type="sbml", bngl_str=str(self)
)
):
raise BNGModelError(
self.model_path,
message="SBML couldn't be generated for libRR simulator",
)
# TODO: Only clear the writeSBML action
# by adding a mechanism to do so
self.actions.clear_actions()
# get the simulator
import bionetgen as bng
self.simulator = bng.sim_getter(
model_str=tpath.read(), sim_type=sim_type
)
# let's deal with observables here
selections = ["time"] + [obs for obs in self.observables]
self.simulator.simulator.timeCourseSelections = selections
self.actions = curr_actions
elif sim_type == "cpy":
# get the simulator
import bionetgen as bng
self.simulator = bng.sim_getter(model_file=self, sim_type=sim_type)
return self.simulator
else:
print(
'Sim type {} is not recognized, only libroadrunner \
is supported currently by passing "libRR" to \
sim_type keyword argument'.format(
sim_type
)
)
return None
# for now we return the underlying simulator
return self.simulator.simulator
###### CORE OBJECT AND PARSING FRONT-END ######